Suggest/invite members
parent
d41eaa07cc
commit
160bff7783
13
.travis.yml
13
.travis.yml
|
@ -1,12 +1,17 @@
|
|||
language: node_js
|
||||
dist: trusty
|
||||
node_js:
|
||||
- "node"
|
||||
before_install:
|
||||
- export CHROME_BIN=chromium-browser
|
||||
- export DISPLAY=:99.0
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
- travis_retry npm install -g gulp
|
||||
- sudo apt-get update
|
||||
- sudo apt-get install -y libappindicator1 fonts-liberation
|
||||
- wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||
- sudo dpkg -i google-chrome*.deb
|
||||
install:
|
||||
- travis_retry npm install
|
||||
before_script:
|
||||
- export CHROME_BIN=/usr/bin/google-chrome
|
||||
- export DISPLAY=:99.0
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
- travis_retry npm install -g gulp
|
||||
- gulp deploy
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
- Add rich text custom fields (with a wysiwyg editor like descreption or comments).
|
||||
- Add thumbnails and preview for PSD files.
|
||||
- Add thumbnails and preview for SVG files.
|
||||
- Improve add-members form: Now users can select between their contacts or type an email.
|
||||
- i18n:
|
||||
- Add japanese (ja) translation.
|
||||
- Add korean (ko) translation.
|
||||
|
|
|
@ -27,112 +27,6 @@ debounce = @.taiga.debounce
|
|||
|
||||
module = angular.module("taigaKanban")
|
||||
|
||||
#############################################################################
|
||||
## Create Members Lightbox Directive
|
||||
#############################################################################
|
||||
|
||||
class LightboxAddMembersController
|
||||
@.$inject = [
|
||||
"$scope",
|
||||
"lightboxService",
|
||||
"tgLoader",
|
||||
"$tgConfirm",
|
||||
"$tgResources",
|
||||
"$rootScope",
|
||||
]
|
||||
|
||||
constructor: (@scope, @lightboxService, @tgLoader, @confirm, @rs, @rootScope) ->
|
||||
@._defaultMaxInvites = 4
|
||||
@._defaultRole = @.project.roles[0].id
|
||||
@.form = null
|
||||
@.submitInvites = false
|
||||
@.canAddUsers = true
|
||||
@.memberInvites = []
|
||||
|
||||
if @.project.max_memberships == null
|
||||
@.membersLimit = @._defaultMaxInvites
|
||||
else
|
||||
pendingMembersCount = Math.max(@.project.max_memberships - @.project.total_memberships, 0)
|
||||
@.membersLimit = Math.min(pendingMembersCount, @._defaultMaxInvites)
|
||||
|
||||
@.addSingleMember()
|
||||
|
||||
addSingleMember: () ->
|
||||
@.memberInvites.push({email:'', role_id: @._defaultRole})
|
||||
|
||||
if @.memberInvites.length >= @.membersLimit
|
||||
@.canAddUsers = false
|
||||
@.showWarningMessage = (!@.canAddUsers &&
|
||||
@.project.total_memberships + @.memberInvites.length == @.project.max_memberships)
|
||||
|
||||
removeSingleMember: (index) ->
|
||||
@.memberInvites.splice(index, 1)
|
||||
|
||||
@.canAddUsers = true
|
||||
@.showWarningMessage = @.membersLimit == 1
|
||||
|
||||
submit: () ->
|
||||
# Need to reset the form constrains
|
||||
@.form.initializeFields()
|
||||
@.form.reset()
|
||||
return if not @.form.validate()
|
||||
|
||||
@.memberInvites = _.filter(@.memberInvites, (invites) ->
|
||||
invites.email != "")
|
||||
|
||||
@.submitInvites = true
|
||||
promise = @rs.memberships.bulkCreateMemberships(
|
||||
@.project.id,
|
||||
@.memberInvites,
|
||||
@.invitationText
|
||||
)
|
||||
promise.then(
|
||||
@._onSuccessInvite.bind(this),
|
||||
@._onErrorInvite.bind(this)
|
||||
)
|
||||
|
||||
_onSuccessInvite: () ->
|
||||
@.submitInvites = false
|
||||
@rootScope.$broadcast("membersform:new:success")
|
||||
@lightboxService.closeAll()
|
||||
@confirm.notify("success")
|
||||
|
||||
_onErrorInvite: (response) ->
|
||||
@.submitInvites = false
|
||||
errors = {}
|
||||
_.each response.data.bulk_memberships, (value, index) =>
|
||||
if value.email
|
||||
errors["email-#{index}"] = value.email[0]
|
||||
if value.role
|
||||
errors["role-#{index}"] = value.role[0]
|
||||
|
||||
@.form.setErrors(errors)
|
||||
if response.data._error_message
|
||||
@confirm.notify("error", response.data._error_message)
|
||||
|
||||
module.controller("LbAddMembersController", LightboxAddMembersController)
|
||||
|
||||
|
||||
|
||||
LightboxAddMembersDirective = (lightboxService) ->
|
||||
link = (scope, el, attrs, ctrl) ->
|
||||
lightboxService.open(el)
|
||||
ctrl.form = el.find("form").checksley()
|
||||
|
||||
return {
|
||||
scope: {},
|
||||
bindToController: {
|
||||
project: '=',
|
||||
},
|
||||
controller: 'LbAddMembersController',
|
||||
controllerAs: 'vm',
|
||||
templateUrl: 'admin/lightbox-add-members.html',
|
||||
link: link
|
||||
}
|
||||
|
||||
module.directive("tgLbAddMembers", ["lightboxService", LightboxAddMembersDirective])
|
||||
|
||||
|
||||
#############################################################################
|
||||
## Warning message directive
|
||||
#############################################################################
|
||||
|
|
|
@ -222,6 +222,8 @@ _.mixin
|
|||
isImage = (name) ->
|
||||
return name.match(/\.(jpe?g|png|gif|gifv|webm|svg|psd)/i) != null
|
||||
|
||||
isEmail = (name) ->
|
||||
return name? and name.match(/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/) != null
|
||||
|
||||
isPdf = (name) ->
|
||||
return name.match(/\.(pdf)/i) != null
|
||||
|
@ -286,6 +288,7 @@ taiga.stripTags = stripTags
|
|||
taiga.replaceTags = replaceTags
|
||||
taiga.defineImmutableProperty = defineImmutableProperty
|
||||
taiga.isImage = isImage
|
||||
taiga.isEmail = isEmail
|
||||
taiga.isPdf = isPdf
|
||||
taiga.patch = patch
|
||||
taiga.getRandomDefaultColor = getRandomDefaultColor
|
||||
|
|
|
@ -990,6 +990,12 @@
|
|||
},
|
||||
"ADD_MEMBER": {
|
||||
"TITLE": "New Member",
|
||||
"PLACEHOLDER": "Filter users or write an email to invite",
|
||||
"ADD_EMAIL": "Add email",
|
||||
"REMOVE": "Remove",
|
||||
"INVITE": "Invite",
|
||||
"CHOOSE_ROLE": "Choose a role",
|
||||
"PLACEHOLDER_INVITATION_TEXT": "(Optional) Add a personalized text to the invitation. Tell something lovely to your new members ;-)",
|
||||
"HELP_TEXT": "If users are already registered on Taiga, they will be added automatically. Otherwise they will receive an invitation."
|
||||
},
|
||||
"CREATE_ISSUE": {
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
###
|
||||
# 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: add-members.controller.coffee
|
||||
###
|
||||
|
||||
taiga = @.taiga
|
||||
|
||||
|
||||
class InviteMembersFormController
|
||||
@.$inject = [
|
||||
"tgProjectService",
|
||||
"$tgResources",
|
||||
"lightboxService",
|
||||
"$tgConfirm",
|
||||
"$rootScope"
|
||||
]
|
||||
|
||||
constructor: (@projectService, @rs, @lightboxService, @confirm, @rootScope) ->
|
||||
@.project = @projectService.project
|
||||
@.roles = @projectService.project.get('roles')
|
||||
@.rolesValues = {}
|
||||
@.loading = false
|
||||
@.defaultMaxInvites = 4
|
||||
|
||||
_areRolesValidated: () ->
|
||||
Object.defineProperty @, 'areRolesValidated', {
|
||||
get: () =>
|
||||
roleIds = _.filter Object.values(@.rolesValues), (it) -> return it
|
||||
return roleIds.length == @.contactsToInvite.size + @.emailsToInvite.size
|
||||
}
|
||||
|
||||
_checkLimitMemberships: () ->
|
||||
if @.project.get('max_memberships') == null
|
||||
@.membersLimit = @.defaultMaxInvites
|
||||
else
|
||||
pendingMembersCount = Math.max(@.project.get('max_memberships') - @.project.get('total_memberships'), 0)
|
||||
@.membersLimit = Math.min(pendingMembersCount, @.defaultMaxInvites)
|
||||
|
||||
@.showWarningMessage = @.membersLimit < @.defaultMaxInvites
|
||||
|
||||
sendInvites: () ->
|
||||
@.setInvitedContacts = []
|
||||
_.forEach(@.rolesValues, (key, value) =>
|
||||
@.setInvitedContacts.push({
|
||||
'role_id': key
|
||||
'username': value
|
||||
})
|
||||
)
|
||||
@.loading = true
|
||||
@rs.memberships.bulkCreateMemberships(
|
||||
@.project.get('id'),
|
||||
@.setInvitedContacts,
|
||||
@.inviteContactsMessage
|
||||
)
|
||||
.then (response) => # On success
|
||||
@.loading = false
|
||||
@lightboxService.closeAll()
|
||||
@rootScope.$broadcast("membersform:new:success")
|
||||
@confirm.notify('success')
|
||||
.catch (response) => # On error
|
||||
@.loading = false
|
||||
if response.data._error_message
|
||||
@confirm.notify("error", response.data._error_message)
|
||||
|
||||
|
||||
angular.module("taigaAdmin").controller("InviteMembersFormCtrl", InviteMembersFormController)
|
|
@ -0,0 +1,134 @@
|
|||
###
|
||||
# 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: invite-members-form.controller.spec.coffee
|
||||
###
|
||||
|
||||
describe "InviteMembersFormController", ->
|
||||
inviteMembersFormCtrl = null
|
||||
provide = null
|
||||
controller = null
|
||||
mocks = {}
|
||||
|
||||
_mockProjectService = () ->
|
||||
mocks.projectService = {
|
||||
project: sinon.stub()
|
||||
}
|
||||
|
||||
provide.value "tgProjectService", mocks.projectService
|
||||
|
||||
_mockTgResources = () ->
|
||||
mocks.tgResources = {
|
||||
memberships: {
|
||||
bulkCreateMemberships: sinon.stub()
|
||||
}
|
||||
}
|
||||
|
||||
provide.value "$tgResources", mocks.tgResources
|
||||
|
||||
_mockLightboxService = () ->
|
||||
mocks.lightboxService = {
|
||||
closeAll: sinon.stub()
|
||||
}
|
||||
|
||||
provide.value "lightboxService", mocks.lightboxService
|
||||
|
||||
_mockTgConfirm = () ->
|
||||
mocks.tgConfirm = {
|
||||
notify: sinon.stub()
|
||||
}
|
||||
|
||||
provide.value "$tgConfirm", mocks.tgConfirm
|
||||
|
||||
_mockRootScope = ->
|
||||
mocks.rootScope = {
|
||||
$broadcast: sinon.stub()
|
||||
}
|
||||
|
||||
provide.value("$rootScope", mocks.rootScope)
|
||||
|
||||
_mocks = () ->
|
||||
module ($provide) ->
|
||||
provide = $provide
|
||||
_mockProjectService()
|
||||
_mockTgResources()
|
||||
_mockLightboxService()
|
||||
_mockTgConfirm()
|
||||
_mockRootScope()
|
||||
return null
|
||||
|
||||
beforeEach ->
|
||||
module "taigaAdmin"
|
||||
|
||||
_mocks()
|
||||
|
||||
inject ($controller) ->
|
||||
controller = $controller
|
||||
|
||||
mocks.projectService.project = Immutable.fromJS([{
|
||||
'roles': 'role1'
|
||||
}])
|
||||
|
||||
it "check limit memberships - no limit", () ->
|
||||
inviteMembersFormCtrl = controller "InviteMembersFormCtrl"
|
||||
|
||||
inviteMembersFormCtrl.project = Immutable.fromJS({
|
||||
'max_memberships': null,
|
||||
})
|
||||
|
||||
inviteMembersFormCtrl.defaultMaxInvites = 4
|
||||
|
||||
inviteMembersFormCtrl._checkLimitMemberships()
|
||||
expect(inviteMembersFormCtrl.membersLimit).to.be.equal(4)
|
||||
expect(inviteMembersFormCtrl.showWarningMessage).to.be.false
|
||||
|
||||
it "check limit memberships", () ->
|
||||
inviteMembersFormCtrl = controller "InviteMembersFormCtrl"
|
||||
|
||||
inviteMembersFormCtrl.project = Immutable.fromJS({
|
||||
'max_memberships': 15,
|
||||
'total_memberships': 13
|
||||
})
|
||||
inviteMembersFormCtrl.defaultMaxInvites = 4
|
||||
|
||||
inviteMembersFormCtrl._checkLimitMemberships()
|
||||
expect(inviteMembersFormCtrl.membersLimit).to.be.equal(2)
|
||||
expect(inviteMembersFormCtrl.showWarningMessage).to.be.true
|
||||
|
||||
|
||||
it "send invites", (done) ->
|
||||
inviteMembersFormCtrl = controller "InviteMembersFormCtrl"
|
||||
inviteMembersFormCtrl.project = Immutable.fromJS(
|
||||
{'id': 1}
|
||||
)
|
||||
inviteMembersFormCtrl.rolesValues = {'user1': 1}
|
||||
inviteMembersFormCtrl.inviteContactsMessage = 'Message'
|
||||
inviteMembersFormCtrl.loading = true
|
||||
|
||||
promise = mocks.tgResources.memberships.bulkCreateMemberships.withArgs(
|
||||
1,
|
||||
[{
|
||||
'role_id': 1
|
||||
'username': 'user1'
|
||||
}],
|
||||
'Message'
|
||||
).promise().resolve()
|
||||
|
||||
inviteMembersFormCtrl.sendInvites().then () ->
|
||||
expect(inviteMembersFormCtrl.loading).to.be.false
|
||||
expect(mocks.rootScope.$broadcast).to.have.been.calledWith("membersform:new:success")
|
||||
expect(mocks.tgConfirm.notify).to.have.been.calledWith("success")
|
||||
done()
|
|
@ -0,0 +1,41 @@
|
|||
###
|
||||
# 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: invite-members.directive.coffee
|
||||
###
|
||||
|
||||
InviteMembersFormDirective = () ->
|
||||
link = (scope, el, attrs, ctrl) ->
|
||||
ctrl._areRolesValidated()
|
||||
ctrl._checkLimitMemberships()
|
||||
|
||||
return {
|
||||
scope: {},
|
||||
templateUrl:"invite-members/invite-members-form/invite-members-form.html",
|
||||
controller: "InviteMembersFormCtrl",
|
||||
controllerAs: "vm",
|
||||
bindToController: {
|
||||
contactsToInvite: '<',
|
||||
emailsToInvite: '=',
|
||||
onDisplayContactList: '&',
|
||||
onRemoveInvitedContact: '&',
|
||||
onRemoveInvitedEmail: '&',
|
||||
onSendInvites: '&'
|
||||
},
|
||||
link: link
|
||||
}
|
||||
|
||||
angular.module("taigaAdmin").directive("tgInviteMembersForm", InviteMembersFormDirective)
|
|
@ -0,0 +1,69 @@
|
|||
form.invite-members-form(ng-submit="vm.sendInvites(vm.inviteContacts)")
|
||||
ul.invite-members-form-list
|
||||
li.invite-members-single.e2e-invite-members-single(
|
||||
ng-repeat="contact in vm.contactsToInvite | toMutable track by contact.id"
|
||||
)
|
||||
.invite-members-single-data
|
||||
img.invite-members-single-avatar(
|
||||
tg-avatar="contact"
|
||||
alt="{{contact.full_name}}"
|
||||
)
|
||||
span.invite-members-single-name {{contact.full_name}}
|
||||
a.invite-members-single-remove.e2e-invite-members-single-remove(
|
||||
href=""
|
||||
ng-click="vm.onRemoveInvitedContact({contact: contact})"
|
||||
translate="LIGHTBOX.ADD_MEMBER.REMOVE"
|
||||
)
|
||||
select.invite-members-single-role.e2e-invite-members-single-role(
|
||||
ng-model="vm.rolesValues[contact.username]"
|
||||
id="add-member-suggest-role-dropdown"
|
||||
ng-options="role.id as role.name for role in vm.roles | toMutable track by role.id"
|
||||
required
|
||||
)
|
||||
option(
|
||||
value=""
|
||||
selected="selected"
|
||||
translate="LIGHTBOX.ADD_MEMBER.CHOOSE_ROLE"
|
||||
)
|
||||
li.invite-members-single.e2e-invite-members-single(
|
||||
ng-repeat="userMail in vm.emailsToInvite | toMutable"
|
||||
)
|
||||
.invite-members-single-data
|
||||
span.invite-members-single-email {{userMail.email}}
|
||||
a.invite-members-single-remove.e2e-invite-members-single-remove(
|
||||
href=""
|
||||
ng-click="vm.onRemoveInvitedEmail({email: userMail})"
|
||||
translate="LIGHTBOX.ADD_MEMBER.REMOVE"
|
||||
)
|
||||
select.invite-members-single-role.e2e-invite-members-single-role(
|
||||
ng-model="vm.rolesValues[userMail.email]"
|
||||
id="add-email-suggest-role-dropdown"
|
||||
ng-options="role.id as role.name for role in vm.roles | toMutable track by role.id"
|
||||
required
|
||||
)
|
||||
option(
|
||||
value=""
|
||||
translate="LIGHTBOX.ADD_MEMBER.CHOOSE_ROLE"
|
||||
)
|
||||
.invite-members-single-new.e2e-invite-members-single-new(
|
||||
ng-if="vm.contactsToInvite.size + vm.emailsToInvite.size < vm.membersLimit"
|
||||
)
|
||||
tg-svg.invite-members-single-new-btn(
|
||||
svg-icon="icon-add"
|
||||
ng-click="vm.onDisplayContactList()"
|
||||
)
|
||||
tg-lightbox-add-members-warning-message(
|
||||
ng-if="vm.showWarningMessage"
|
||||
project="vm.project"
|
||||
)
|
||||
textarea.invite-members-single-msg(
|
||||
ng-model="vm.inviteContactsMessage"
|
||||
placeholder="{{'LIGHTBOX.ADD_MEMBER.PLACEHOLDER_INVITATION_TEXT' | translate}}"
|
||||
)
|
||||
button.button-green.invite-members-single-send.e2e-invite-members-single-send(
|
||||
type="submit"
|
||||
translate="LIGHTBOX.ADD_MEMBER.INVITE"
|
||||
ng-disabled="!vm.areRolesValidated"
|
||||
tg-loading="vm.loading"
|
||||
)
|
||||
p.invite-members-single-help(translate="LIGHTBOX.ADD_MEMBER.HELP_TEXT")
|
|
@ -0,0 +1,67 @@
|
|||
.invite-members-form {
|
||||
border-top: 1px solid $whitish;
|
||||
margin: 0 5rem;
|
||||
.invite-members-form-list {
|
||||
margin: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.invite-members-single {
|
||||
align-items: center;
|
||||
border-bottom: 1px solid $whitish;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 1rem;
|
||||
}
|
||||
.invite-members-single-data {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
.invite-members-single-avatar {
|
||||
height: 4rem;
|
||||
margin-right: 1rem;
|
||||
width: 4rem;
|
||||
}
|
||||
.invite-members-single-remove {
|
||||
color: $red-light;
|
||||
margin-left: 1rem;
|
||||
transition: color .2s;
|
||||
&:hover {
|
||||
color: $red;
|
||||
}
|
||||
}
|
||||
.invite-members-single-role {
|
||||
flex-basis: 40%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.invite-members-single-new {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 1rem 0;
|
||||
.invite-members-single-new-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
.icon-add {
|
||||
@include svg-size(2rem);
|
||||
fill: $grayer;
|
||||
transition: fill .2s;
|
||||
}
|
||||
&:hover {
|
||||
.icon-add {
|
||||
fill: $primary-light;
|
||||
}
|
||||
}
|
||||
}
|
||||
.invite-members-single-send {
|
||||
@include font-size(large);
|
||||
display: block;
|
||||
margin: 1.5rem 0 1rem;
|
||||
padding: 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
.invite-members-single-help {
|
||||
@include font-size(small);
|
||||
@include font-type(light);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
###
|
||||
# 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: add-members.controller.coffee
|
||||
###
|
||||
|
||||
taiga = @.taiga
|
||||
|
||||
class AddMembersController
|
||||
@.$inject = [
|
||||
"tgUserService",
|
||||
"tgCurrentUserService",
|
||||
"tgProjectService",
|
||||
]
|
||||
|
||||
constructor: (@userService, @currentUserService, @projectService) ->
|
||||
@.contactsToInvite = Immutable.List()
|
||||
@.emailsToInvite = Immutable.List()
|
||||
@.displayContactList = false
|
||||
|
||||
_getContacts: () ->
|
||||
userId = @currentUserService.getUser().get("id")
|
||||
excludeProjectId = @projectService.project.get("id")
|
||||
|
||||
@userService.getContacts(userId, excludeProjectId).then (contacts) =>
|
||||
@.contacts = contacts
|
||||
|
||||
_filterContacts: (invited) ->
|
||||
@.contacts = @.contacts.filter( (contact) =>
|
||||
contact.get('id') != invited.get('id')
|
||||
)
|
||||
|
||||
inviteSuggested: (contact) ->
|
||||
@.contactsToInvite = @.contactsToInvite.push(contact)
|
||||
@._filterContacts(contact)
|
||||
@.displayContactList = true
|
||||
|
||||
removeContact: (invited) ->
|
||||
@.contactsToInvite = @.contactsToInvite.filter( (contact) =>
|
||||
return contact.get('id') != invited.id
|
||||
)
|
||||
invited = Immutable.fromJS(invited)
|
||||
@.contacts = @.contacts.push(invited)
|
||||
@.testEmptyContacts()
|
||||
|
||||
inviteEmail: (email) ->
|
||||
emailData = Immutable.Map({'email': email})
|
||||
@.emailsToInvite = @.emailsToInvite.push(emailData)
|
||||
@.displayContactList = true
|
||||
|
||||
removeEmail: (invited) ->
|
||||
@.emailsToInvite = @.emailsToInvite.filter( (email) =>
|
||||
return email.get('email') != invited.email
|
||||
)
|
||||
@.testEmptyContacts()
|
||||
|
||||
testEmptyContacts: () ->
|
||||
if @.emailsToInvite.size + @.contactsToInvite.size == 0
|
||||
@.displayContactList = false
|
||||
|
||||
angular.module("taigaAdmin").controller("AddMembersCtrl", AddMembersController)
|
|
@ -0,0 +1,180 @@
|
|||
###
|
||||
# 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: lightbox-add-members.controller.spec.coffee
|
||||
###
|
||||
|
||||
describe "AddMembersController", ->
|
||||
addMembersCtrl = null
|
||||
provide = null
|
||||
controller = null
|
||||
mocks = {}
|
||||
|
||||
_mockUserService = () ->
|
||||
mocks.userService = {
|
||||
getContacts: sinon.stub()
|
||||
}
|
||||
|
||||
provide.value "tgUserService", mocks.userService
|
||||
|
||||
_mockCurrentUser = () ->
|
||||
mocks.currentUser = {
|
||||
getUser: sinon.stub()
|
||||
}
|
||||
|
||||
provide.value "tgCurrentUserService", mocks.currentUser
|
||||
|
||||
_mockProjectService = () ->
|
||||
mocks.projectService = {
|
||||
project: sinon.stub()
|
||||
}
|
||||
|
||||
provide.value "tgProjectService", mocks.projectService
|
||||
|
||||
_mocks = () ->
|
||||
module ($provide) ->
|
||||
provide = $provide
|
||||
_mockCurrentUser()
|
||||
_mockUserService()
|
||||
_mockProjectService()
|
||||
return null
|
||||
|
||||
beforeEach ->
|
||||
module "taigaAdmin"
|
||||
|
||||
_mocks()
|
||||
|
||||
inject ($controller) ->
|
||||
controller = $controller
|
||||
|
||||
|
||||
it "get user contacts", (done) ->
|
||||
|
||||
userId = 1
|
||||
excludeProjectId = 1
|
||||
|
||||
mocks.currentUser.getUser.returns(Immutable.fromJS({
|
||||
id: userId
|
||||
}))
|
||||
mocks.projectService.project = Immutable.fromJS({
|
||||
id: excludeProjectId
|
||||
})
|
||||
|
||||
contacts = Immutable.fromJS({
|
||||
username: "username",
|
||||
full_name_display: "full-name-display",
|
||||
bio: "bio"
|
||||
})
|
||||
|
||||
mocks.userService.getContacts.withArgs(userId, excludeProjectId).promise().resolve(contacts)
|
||||
|
||||
addMembersCtrl = controller "AddMembersCtrl"
|
||||
|
||||
addMembersCtrl._getContacts().then () ->
|
||||
expect(addMembersCtrl.contacts).to.be.equal(contacts)
|
||||
done()
|
||||
|
||||
it "filterContacts", () ->
|
||||
|
||||
addMembersCtrl = controller "AddMembersCtrl"
|
||||
addMembersCtrl.contacts = Immutable.fromJS([
|
||||
{id: 1}
|
||||
{id: 2}
|
||||
])
|
||||
invited = Immutable.fromJS({id: 1})
|
||||
|
||||
addMembersCtrl._filterContacts(invited)
|
||||
|
||||
expect(addMembersCtrl.contacts.size).to.be.equal(1)
|
||||
|
||||
it "invite suggested", () ->
|
||||
addMembersCtrl = controller "AddMembersCtrl"
|
||||
addMembersCtrl.contactsToInvite = Immutable.List()
|
||||
addMembersCtrl.displayContactList = false
|
||||
|
||||
contact = Immutable.fromJS({id: 1})
|
||||
|
||||
addMembersCtrl._filterContacts = sinon.stub()
|
||||
|
||||
addMembersCtrl.inviteSuggested(contact)
|
||||
expect(addMembersCtrl.contactsToInvite.size).to.be.equal(1)
|
||||
expect(addMembersCtrl._filterContacts).to.be.calledWith(contact)
|
||||
expect(addMembersCtrl.displayContactList).to.be.true
|
||||
|
||||
it "remove contact", () ->
|
||||
addMembersCtrl = controller "AddMembersCtrl"
|
||||
addMembersCtrl.contactsToInvite = Immutable.fromJS([
|
||||
{id: 1}
|
||||
{id: 2}
|
||||
])
|
||||
invited = {id: 1}
|
||||
addMembersCtrl.contacts = Immutable.fromJS([])
|
||||
|
||||
addMembersCtrl.testEmptyContacts = sinon.stub()
|
||||
|
||||
addMembersCtrl.removeContact(invited)
|
||||
expect(addMembersCtrl.contactsToInvite.size).to.be.equal(1)
|
||||
expect(addMembersCtrl.contacts.size).to.be.equal(1)
|
||||
expect(addMembersCtrl.testEmptyContacts).to.be.called
|
||||
|
||||
it "invite email", () ->
|
||||
addMembersCtrl = controller "AddMembersCtrl"
|
||||
email = 'email@example.com'
|
||||
emailData = Immutable.Map({'email': email})
|
||||
addMembersCtrl.displayContactList = false
|
||||
|
||||
addMembersCtrl.emailsToInvite = Immutable.fromJS([])
|
||||
|
||||
addMembersCtrl.inviteEmail(email)
|
||||
expect(emailData.get('email')).to.be.equal(email)
|
||||
expect(addMembersCtrl.emailsToInvite.size).to.be.equal(1)
|
||||
expect(addMembersCtrl.displayContactList).to.be.true
|
||||
|
||||
it "remove email", () ->
|
||||
addMembersCtrl = controller "AddMembersCtrl"
|
||||
invited = {email: 'email@example.com'}
|
||||
addMembersCtrl.emailsToInvite = Immutable.fromJS([
|
||||
{'email': 'email@example.com'}
|
||||
{'email': 'email@example2.com'}
|
||||
])
|
||||
|
||||
addMembersCtrl.testEmptyContacts = sinon.stub()
|
||||
|
||||
addMembersCtrl.removeEmail(invited)
|
||||
expect(addMembersCtrl.emailsToInvite.size).to.be.equal(1)
|
||||
expect(addMembersCtrl.testEmptyContacts).to.be.called
|
||||
|
||||
it "test empty contacts - not empty", () ->
|
||||
addMembersCtrl = controller "AddMembersCtrl"
|
||||
addMembersCtrl.displayContactList = true
|
||||
addMembersCtrl.emailsToInvite = Immutable.fromJS([
|
||||
{'email': 'email@example.com'}
|
||||
{'email': 'email@example2.com'}
|
||||
])
|
||||
addMembersCtrl.contactsToInvite = Immutable.fromJS([
|
||||
{'id': 1}
|
||||
{'id': 1}
|
||||
])
|
||||
addMembersCtrl.testEmptyContacts()
|
||||
expect(addMembersCtrl.displayContactList).to.be.true
|
||||
|
||||
it "test empty contacts - empty", () ->
|
||||
addMembersCtrl = controller "AddMembersCtrl"
|
||||
addMembersCtrl.displayContactList = true
|
||||
addMembersCtrl.emailsToInvite = Immutable.fromJS([])
|
||||
addMembersCtrl.contactsToInvite = Immutable.fromJS([])
|
||||
addMembersCtrl.testEmptyContacts()
|
||||
expect(addMembersCtrl.displayContactList).to.be.false
|
|
@ -0,0 +1,33 @@
|
|||
###
|
||||
# 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: add-member.directive.coffee
|
||||
###
|
||||
|
||||
LightboxAddMembersDirective = (lightboxService) ->
|
||||
link = (scope, el, attrs, ctrl) ->
|
||||
lightboxService.open(el)
|
||||
ctrl._getContacts()
|
||||
|
||||
return {
|
||||
scope: {},
|
||||
templateUrl:"invite-members/lightbox-add-members.html",
|
||||
controller: "AddMembersCtrl",
|
||||
controllerAs: "vm",
|
||||
link: link
|
||||
}
|
||||
|
||||
angular.module("taigaAdmin").directive("tgLbAddMembers", ["lightboxService", LightboxAddMembersDirective])
|
|
@ -0,0 +1,18 @@
|
|||
tg-lightbox-close
|
||||
.add-members-wrapper
|
||||
h2.title(translate="LIGHTBOX.ADD_MEMBER.TITLE")
|
||||
tg-suggest-add-members(
|
||||
ng-show="!vm.displayContactList"
|
||||
contacts="vm.contacts"
|
||||
on-invite-suggested="vm.inviteSuggested(contact)"
|
||||
on-invite-email="vm.inviteEmail(email)"
|
||||
)
|
||||
tg-invite-members-form(
|
||||
ng-show="vm.displayContactList"
|
||||
on-display-contact-list="vm.displayContactList = false"
|
||||
contacts-to-invite="vm.contactsToInvite"
|
||||
emails-to-invite="vm.emailsToInvite"
|
||||
on-remove-invited-contact="vm.removeContact(contact)"
|
||||
on-remove-invited-email="vm.removeEmail(email)"
|
||||
on-send-invites="vm.submit(invites)"
|
||||
)
|
|
@ -0,0 +1,6 @@
|
|||
.lightbox-add-member {
|
||||
.add-members-wrapper {
|
||||
max-width: 900px;
|
||||
width: 90%;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
###
|
||||
# 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: suggest-add-members.controller.coffee
|
||||
###
|
||||
|
||||
taiga = @.taiga
|
||||
|
||||
class SuggestAddMembersController
|
||||
@.$inject = []
|
||||
|
||||
constructor: () ->
|
||||
@.contactQuery = ""
|
||||
|
||||
isEmail: () ->
|
||||
return taiga.isEmail(@.contactQuery)
|
||||
|
||||
filterContacts: () ->
|
||||
@.filteredContacts = @.contacts.filter( (contact) =>
|
||||
contact.get('full_name_display').toLowerCase().includes(@.contactQuery.toLowerCase()) || contact.get('username').toLowerCase().includes(@.contactQuery.toLowerCase());
|
||||
)
|
||||
|
||||
setInvited: (contact) ->
|
||||
@.onInviteSuggested({'contact': contact})
|
||||
|
||||
angular.module("taigaAdmin").controller("SuggestAddMembersCtrl", SuggestAddMembersController)
|
|
@ -0,0 +1,79 @@
|
|||
###
|
||||
# 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: suggest-add-members.controller.spec.coffee
|
||||
###
|
||||
|
||||
describe "SuggestAddMembersController", ->
|
||||
suggestAddMembersCtrl = null
|
||||
provide = null
|
||||
controller = null
|
||||
mocks = {}
|
||||
|
||||
_mocks = () ->
|
||||
module ($provide) ->
|
||||
provide = $provide
|
||||
return null
|
||||
|
||||
beforeEach ->
|
||||
module "taigaAdmin"
|
||||
|
||||
_mocks()
|
||||
|
||||
inject ($controller) ->
|
||||
controller = $controller
|
||||
|
||||
it "is email - wrong", () ->
|
||||
suggestAddMembersCtrl = controller "SuggestAddMembersCtrl"
|
||||
suggestAddMembersCtrl.contactQuery = 'lololo'
|
||||
|
||||
result = suggestAddMembersCtrl.isEmail()
|
||||
expect(result).to.be.false
|
||||
|
||||
it "is email - true", () ->
|
||||
suggestAddMembersCtrl = controller "SuggestAddMembersCtrl"
|
||||
suggestAddMembersCtrl.contactQuery = 'lololo@lolo.com'
|
||||
|
||||
result = suggestAddMembersCtrl.isEmail()
|
||||
expect(result).to.be.true
|
||||
|
||||
it "filter contacts", () ->
|
||||
suggestAddMembersCtrl = controller "SuggestAddMembersCtrl"
|
||||
suggestAddMembersCtrl.contacts = Immutable.fromJS([
|
||||
{
|
||||
full_name_display: 'Abel Sonofadan'
|
||||
username: 'abel'
|
||||
},
|
||||
{
|
||||
full_name_display: 'Cain Sonofadan'
|
||||
username: 'cain'
|
||||
}
|
||||
])
|
||||
|
||||
suggestAddMembersCtrl.contactQuery = 'Cain Sonofadan'
|
||||
|
||||
suggestAddMembersCtrl.filterContacts()
|
||||
expect(suggestAddMembersCtrl.filteredContacts.size).to.be.equal(1)
|
||||
|
||||
it "set invited", () ->
|
||||
suggestAddMembersCtrl = controller "SuggestAddMembersCtrl"
|
||||
|
||||
contact = 'contact'
|
||||
|
||||
suggestAddMembersCtrl.onInviteSuggested = sinon.stub()
|
||||
|
||||
suggestAddMembersCtrl.setInvited(contact)
|
||||
expect(suggestAddMembersCtrl.onInviteSuggested).has.been.calledWith({'contact': contact})
|
|
@ -0,0 +1,34 @@
|
|||
###
|
||||
# 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: suggest-add-member.directive.coffee
|
||||
###
|
||||
|
||||
SuggestAddMembersDirective = (lightboxService) ->
|
||||
return {
|
||||
scope: {},
|
||||
templateUrl:"invite-members/suggest-add-members/suggest-add-members.html",
|
||||
controller: "SuggestAddMembersCtrl",
|
||||
controllerAs: "vm",
|
||||
bindToController: {
|
||||
contacts: '=',
|
||||
filteredContacts: '<contacts',
|
||||
onInviteSuggested: '&',
|
||||
onInviteEmail: '&'
|
||||
}
|
||||
}
|
||||
|
||||
angular.module("taigaAdmin").directive("tgSuggestAddMembers", SuggestAddMembersDirective)
|
|
@ -0,0 +1,31 @@
|
|||
.add-member-suggest
|
||||
form.add-member-suggest-filter
|
||||
input.add-member-suggest-filter-input(
|
||||
type="text"
|
||||
ng-model="vm.contactQuery"
|
||||
placeholder="{{'LIGHTBOX.ADD_MEMBER.PLACEHOLDER' | translate}}"
|
||||
ng-keyup="vm.filterContacts()"
|
||||
)
|
||||
|
||||
span.add-member-suggest-filter-hint(
|
||||
ng-if="!vm.filteredContacts.size"
|
||||
ng-class="{'to-send': vm.isEmail()}"
|
||||
translate="LIGHTBOX.ADD_MEMBER.ADD_EMAIL"
|
||||
)
|
||||
|
||||
button.add-member-suggest-filter-addmail.e2e-add-member-suggest-filter-addmail(
|
||||
ng-click="vm.onInviteEmail({email: vm.contactQuery})"
|
||||
ng-if="vm.isEmail()"
|
||||
)
|
||||
tg-svg(svg-icon="icon-add-user")
|
||||
|
||||
ul.add-member-suggest-list
|
||||
li.add-member-suggest-single.e2e-add-member-suggest-single(
|
||||
tg-repeat="contact in vm.filteredContacts"
|
||||
ng-click="vm.setInvited(contact)"
|
||||
)
|
||||
img.add-member-suggest-avatar(
|
||||
tg-avatar="contact"
|
||||
alt="{{contact.get('full_name_display')}}"
|
||||
)
|
||||
span.add-member-suggest-name {{contact.get('full_name_display')}}
|
|
@ -0,0 +1,78 @@
|
|||
.add-member-suggest {
|
||||
.add-member-suggest-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 2rem 0 0;
|
||||
}
|
||||
|
||||
.add-member-suggest-filter {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
padding: 0 15rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.add-member-suggest-filter-input {
|
||||
flex: 1;
|
||||
margin-right: .25rem;
|
||||
}
|
||||
|
||||
.add-member-suggest-filter-hint {
|
||||
@include font-size(xsmall);
|
||||
color: $gray-light;
|
||||
position: absolute;
|
||||
right: 16rem;
|
||||
top: .5rem;
|
||||
&.to-send {
|
||||
right: 19rem;
|
||||
}
|
||||
}
|
||||
|
||||
.add-member-suggest-filter-addmail {
|
||||
background: $grayer;
|
||||
border-radius: .25rem;
|
||||
padding: .5rem .75rem;
|
||||
transition: background .2s linear;
|
||||
|
||||
&:hover {
|
||||
background: $blackish;
|
||||
}
|
||||
|
||||
svg {
|
||||
@include svg-size(1.3rem);
|
||||
fill: $white;
|
||||
}
|
||||
}
|
||||
|
||||
.add-member-suggest-single {
|
||||
align-items: center;
|
||||
background: $white;
|
||||
border-bottom: 1px solid $whitish;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-basis: calc(25% - 1rem);
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
margin-right: 1rem;
|
||||
padding: .2rem;
|
||||
transition: .2s linear;
|
||||
|
||||
&:hover {
|
||||
background: rgba($primary-light, .1);
|
||||
}
|
||||
|
||||
&:nth-child(4n) {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.add-member-suggest-avatar {
|
||||
height: 5rem;
|
||||
margin: .5rem;
|
||||
width: 5rem;
|
||||
}
|
||||
|
||||
.add-member-suggest-name {
|
||||
@include font-type(light);
|
||||
}
|
||||
}
|
|
@ -50,16 +50,19 @@ Resource = (urlsService, http, paginateResponseService) ->
|
|||
.then (result) ->
|
||||
return Immutable.fromJS(result.data)
|
||||
|
||||
service.getContacts = (userId) ->
|
||||
service.getContacts = (userId, excludeProjectId) ->
|
||||
url = urlsService.resolve("user-contacts", userId)
|
||||
|
||||
params = {}
|
||||
params.exclude_project = excludeProjectId if excludeProjectId?
|
||||
|
||||
httpOptions = {
|
||||
headers: {
|
||||
"x-disable-pagination": "1"
|
||||
}
|
||||
}
|
||||
|
||||
return http.get(url, {}, httpOptions)
|
||||
return http.get(url, params, httpOptions)
|
||||
.then (result) ->
|
||||
return Immutable.fromJS(result.data)
|
||||
|
||||
|
|
|
@ -30,8 +30,8 @@ class UserService extends taiga.Service
|
|||
getUserByUserName: (username) ->
|
||||
return @rs.users.getUserByUsername(username)
|
||||
|
||||
getContacts: (userId) ->
|
||||
return @rs.users.getContacts(userId)
|
||||
getContacts: (userId, excludeProjectId) ->
|
||||
return @rs.users.getContacts(userId, excludeProjectId)
|
||||
|
||||
getLiked: (userId, pageNumber, objectType, textQuery) ->
|
||||
return @rs.users.getLiked(userId, pageNumber, objectType, textQuery)
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
p.member-limit-warning(
|
||||
ng-if="project.i_am_owner == true"
|
||||
ng-if="project.get('i_am_owner') == true"
|
||||
translate="LIGHTBOX.CREATE_MEMBER.LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER"
|
||||
translate-values="{maxMembers: project.max_memberships}"
|
||||
translate-values="{maxMembers: project.get('max_memberships')}"
|
||||
)
|
||||
|
||||
p.member-limit-warning(
|
||||
ng-if="project.i_am_owner == false"
|
||||
ng-if="project.get('i_am_owner') == false"
|
||||
translate="LIGHTBOX.CREATE_MEMBER.LIMIT_USERS_WARNING_MESSAGE"
|
||||
translate-values="{maxMembers: project.max_memberships}"
|
||||
translate-values="{maxMembers: project.get('max_memberships')}"
|
||||
)
|
||||
|
|
|
@ -143,83 +143,6 @@
|
|||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.lightbox-add-member {
|
||||
.add-member-wrapper {
|
||||
max-width: 600px;
|
||||
width: 90%;
|
||||
}
|
||||
.add-single-member {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: .5rem;
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
fieldset {
|
||||
display: inline-block;
|
||||
flex: 1;
|
||||
margin: 0 .5rem 0 0;
|
||||
&:last-child {
|
||||
flex-basis: 30px;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
&:first-child {
|
||||
flex-basis: 20%;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
.icon {
|
||||
@include svg-size(1.25rem);
|
||||
fill: $gray;
|
||||
margin-left: .5rem;
|
||||
}
|
||||
.icon-add {
|
||||
&:hover {
|
||||
fill: $primary;
|
||||
transition: fill .2s;
|
||||
}
|
||||
}
|
||||
.icon-trash {
|
||||
fill: $red-light;
|
||||
&:hover {
|
||||
fill: $red;
|
||||
transition: fill .2s;
|
||||
}
|
||||
}
|
||||
.member-limit-warning {
|
||||
@include font-size(small);
|
||||
background: $mass-white;
|
||||
color: $grayer;
|
||||
margin: 1rem 0;
|
||||
padding: 1rem 2rem;
|
||||
text-align: center;
|
||||
a {
|
||||
color: $primary;
|
||||
&:hover {
|
||||
color: $primary-light;
|
||||
}
|
||||
}
|
||||
}
|
||||
.help-text {
|
||||
@include font-size(small);
|
||||
@include font-type(light);
|
||||
margin-top: 1rem;
|
||||
}
|
||||
.checksley-error-list {
|
||||
right: .5rem;
|
||||
li {
|
||||
display: none;
|
||||
&:first-child {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lightbox-sprint-add-edit {
|
||||
form {
|
||||
flex-basis: 600px;
|
||||
|
|
|
@ -18,17 +18,28 @@ helper.getNewMemberLightbox = function() {
|
|||
return utils.lightbox.close(el);
|
||||
},
|
||||
newEmail: function(email) {
|
||||
el.$$('input').last().sendKeys(email);
|
||||
el.$('.add-fieldset').click();
|
||||
el.$$('input').clear();
|
||||
el.$$('input').sendKeys(email);
|
||||
el.$('.e2e-add-member-suggest-filter-addmail').click();
|
||||
},
|
||||
getRows: function() {
|
||||
return el.$$('.add-single-member');
|
||||
addSuggested: function(index) {
|
||||
el.$$('.e2e-add-member-suggest-single').get(index).click();
|
||||
},
|
||||
deleteRow: function(index) {
|
||||
el.$$('.remove-fieldset').get(index).click();
|
||||
addNew: function() {
|
||||
return el.$$('.e2e-invite-members-single-new').click();
|
||||
},
|
||||
setRole: function(index) {
|
||||
let select = el.$$('.e2e-invite-members-single-role').get(index);
|
||||
select.$('option:last-child').click();
|
||||
},
|
||||
getInviteds: function() {
|
||||
return el.$$('.e2e-invite-members-single')
|
||||
},
|
||||
deleteInvited: function(index) {
|
||||
el.$$('.e2e-invite-members-single-remove').get(index).click();
|
||||
},
|
||||
submit: function() {
|
||||
return el.$('.submit-button').click();
|
||||
return el.$('.e2e-invite-members-single-send').click();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -28,25 +28,30 @@ describe('admin - members', function() {
|
|||
adminMembershipsHelper.openNewMemberLightbox();
|
||||
|
||||
await newMemberLightbox.waitOpen();
|
||||
utils.common.takeScreenshot('memberships', 'new-member');
|
||||
utils.common.takeScreenshot('memberships', 'add-new-member');
|
||||
});
|
||||
|
||||
it('add members row', async function() {
|
||||
it('add contacts', async function() {
|
||||
newMemberLightbox.addSuggested(0);
|
||||
newMemberLightbox.addNew();
|
||||
newMemberLightbox.newEmail('xxx' + new Date().getTime() + '@xx.es');
|
||||
newMemberLightbox.addNew();
|
||||
newMemberLightbox.newEmail('xxx' + new Date().getTime() + '@xx.es');
|
||||
newMemberLightbox.newEmail('xxx' + new Date().getTime() + '@xx.es');
|
||||
|
||||
let membersRows = await newMemberLightbox.getRows().count();
|
||||
|
||||
expect(membersRows).to.be.equal(3 + 1);
|
||||
utils.common.takeScreenshot('memberships', 'add-new-member-form');
|
||||
});
|
||||
|
||||
it('delete members row', async function() {
|
||||
newMemberLightbox.deleteRow(2);
|
||||
newMemberLightbox.deleteInvited(2);
|
||||
|
||||
let membersRows = await newMemberLightbox.getRows().count();
|
||||
let invitedRows = await newMemberLightbox.getInviteds().count();
|
||||
|
||||
expect(membersRows).to.be.equal(2 + 1);
|
||||
expect(invitedRows).to.be.equal(2);
|
||||
});
|
||||
|
||||
it('set roles', async function() {
|
||||
newMemberLightbox.setRole(0);
|
||||
newMemberLightbox.setRole(1);
|
||||
utils.common.takeScreenshot('memberships', 'add-new-member-form-active');
|
||||
});
|
||||
|
||||
it('submit', async function() {
|
||||
|
|
Loading…
Reference in New Issue