diff options
Diffstat (limited to 'app/assets/javascripts/invite_members/components')
3 files changed, 74 insertions, 29 deletions
diff --git a/app/assets/javascripts/invite_members/components/invite_members_modal.vue b/app/assets/javascripts/invite_members/components/invite_members_modal.vue index b55ef77ae5d..a92289ca8c1 100644 --- a/app/assets/javascripts/invite_members/components/invite_members_modal.vue +++ b/app/assets/javascripts/invite_members/components/invite_members_modal.vue @@ -9,6 +9,7 @@ import { GlButton, GlFormInput, } from '@gitlab/ui'; +import { partition, isString } from 'lodash'; import eventHub from '../event_hub'; import { s__, __, sprintf } from '~/locale'; import Api from '~/api'; @@ -58,7 +59,7 @@ export default { visible: true, modalId: 'invite-members-modal', selectedAccessLevel: this.defaultAccessLevel, - newUsersToInvite: '', + newUsersToInvite: [], selectedDate: undefined, }; }, @@ -79,13 +80,12 @@ export default { return { onComplete: () => { this.selectedAccessLevel = this.defaultAccessLevel; - this.newUsersToInvite = ''; + this.newUsersToInvite = []; }, }; }, - postData() { + basePostData() { return { - user_id: this.newUsersToInvite, access_level: this.selectedAccessLevel, expires_at: this.selectedDate, format: 'json', @@ -93,7 +93,7 @@ export default { }, selectedRoleName() { return Object.keys(this.accessLevels).find( - key => this.accessLevels[key] === Number(this.selectedAccessLevel), + (key) => this.accessLevels[key] === Number(this.selectedAccessLevel), ); }, }, @@ -101,6 +101,17 @@ export default { eventHub.$on('openModal', this.openModal); }, methods: { + partitionNewUsersToInvite() { + const [usersToInviteByEmail, usersToAddById] = partition( + this.newUsersToInvite, + (user) => isString(user.id) && user.id.includes('user-defined-token'), + ); + + return [ + usersToInviteByEmail.map((user) => user.name).join(','), + usersToAddById.map((user) => user.id).join(','), + ]; + }, openModal() { this.$root.$emit('bv::show::modal', this.modalId); }, @@ -108,7 +119,7 @@ export default { this.$root.$emit('bv::hide::modal', this.modalId); }, sendInvite() { - this.submitForm(this.postData); + this.submitForm(); this.closeModal(); }, cancelInvite() { @@ -120,15 +131,33 @@ export default { changeSelectedItem(item) { this.selectedAccessLevel = item; }, - submitForm(formData) { - if (this.isProject) { - return Api.inviteProjectMembers(this.id, formData) - .then(this.showToastMessageSuccess) - .catch(this.showToastMessageError); + submitForm() { + const [usersToInviteByEmail, usersToAddById] = this.partitionNewUsersToInvite(); + const promises = []; + + if (usersToInviteByEmail !== '') { + const apiInviteByEmail = this.isProject + ? Api.inviteProjectMembersByEmail.bind(Api) + : Api.inviteGroupMembersByEmail.bind(Api); + + promises.push(apiInviteByEmail(this.id, this.inviteByEmailPostData(usersToInviteByEmail))); } - return Api.inviteGroupMember(this.id, formData) - .then(this.showToastMessageSuccess) - .catch(this.showToastMessageError); + + if (usersToAddById !== '') { + const apiAddByUserId = this.isProject + ? Api.addProjectMembersByUserId.bind(Api) + : Api.addGroupMembersByUserId.bind(Api); + + promises.push(apiAddByUserId(this.id, this.addByUserIdPostData(usersToAddById))); + } + + Promise.all(promises).then(this.showToastMessageSuccess).catch(this.showToastMessageError); + }, + inviteByEmailPostData(usersToInviteByEmail) { + return { ...this.basePostData, email: usersToInviteByEmail }; + }, + addByUserIdPostData(usersToAddById) { + return { ...this.basePostData, user_id: usersToAddById }; }, showToastMessageSuccess() { this.$toast.show(this.$options.labels.toastMessageSuccessful, this.toastOptions); @@ -195,7 +224,7 @@ export default { <div class="gl-mt-2"> <gl-sprintf :message="$options.labels.readMoreText"> - <template #link="{content}"> + <template #link="{ content }"> <gl-link :href="helpLink" target="_blank">{{ content }}</gl-link> </template> </gl-sprintf> diff --git a/app/assets/javascripts/invite_members/components/invite_members_trigger.vue b/app/assets/javascripts/invite_members/components/invite_members_trigger.vue index d133e3655e3..eb97c458f88 100644 --- a/app/assets/javascripts/invite_members/components/invite_members_trigger.vue +++ b/app/assets/javascripts/invite_members/components/invite_members_trigger.vue @@ -19,6 +19,11 @@ export default { required: false, default: '', }, + classes: { + type: String, + required: false, + default: '', + }, }, methods: { openModal() { @@ -29,7 +34,7 @@ export default { </script> <template> - <gl-link @click="openModal"> + <gl-link :class="classes" @click="openModal"> <div v-if="icon" class="nav-icon-container"> <gl-icon :size="16" :name="icon" /> </div> diff --git a/app/assets/javascripts/invite_members/components/members_token_select.vue b/app/assets/javascripts/invite_members/components/members_token_select.vue index aed2e5e3236..627d4ab2771 100644 --- a/app/assets/javascripts/invite_members/components/members_token_select.vue +++ b/app/assets/javascripts/invite_members/components/members_token_select.vue @@ -1,14 +1,16 @@ <script> import { debounce } from 'lodash'; -import { GlTokenSelector, GlAvatar, GlAvatarLabeled } from '@gitlab/ui'; +import { GlTokenSelector, GlAvatar, GlAvatarLabeled, GlSprintf } from '@gitlab/ui'; +import { __ } from '~/locale'; import { USER_SEARCH_DELAY } from '../constants'; -import Api from '~/api'; +import { getUsers } from '~/rest_api'; export default { components: { GlTokenSelector, GlAvatar, GlAvatarLabeled, + GlSprintf, }, props: { placeholder: { @@ -32,12 +34,10 @@ export default { }; }, computed: { - newUsersToInvite() { - return this.selectedTokens - .map(obj => { - return obj.id; - }) - .join(','); + emailIsValid() { + const regex = /.+@/; + + return this.query.match(regex) !== null; }, placeholderText() { if (this.selectedTokens.length === 0) { @@ -54,9 +54,9 @@ export default { this.retrieveUsers(query); }, retrieveUsers: debounce(function debouncedRetrieveUsers() { - return Api.users(this.query, this.$options.queryOptions) - .then(response => { - this.users = response.data.map(token => ({ + return getUsers(this.query, this.$options.queryOptions) + .then((response) => { + this.users = response.data.map((token) => ({ id: token.id, name: token.name, username: token.username, @@ -69,7 +69,7 @@ export default { }); }, USER_SEARCH_DELAY), handleInput() { - this.$emit('input', this.newUsersToInvite); + this.$emit('input', this.selectedTokens); }, handleBlur() { this.hideDropdownWithNoItems = false; @@ -86,6 +86,9 @@ export default { }, }, queryOptions: { exclude_internal: true, active: true }, + i18n: { + inviteTextMessage: __('Invite "%{email}" by email'), + }, }; </script> @@ -94,7 +97,7 @@ export default { v-model="selectedTokens" :dropdown-items="users" :loading="loading" - :allow-user-defined-tokens="false" + :allow-user-defined-tokens="emailIsValid" :hide-dropdown-with-no-items="hideDropdownWithNoItems" :placeholder="placeholderText" :aria-labelledby="ariaLabelledby" @@ -116,5 +119,13 @@ export default { :sub-label="dropdownItem.username" /> </template> + + <template #user-defined-token-content="{ inputText: email }"> + <gl-sprintf :message="$options.i18n.inviteTextMessage"> + <template #email> + <span>{{ email }}</span> + </template> + </gl-sprintf> + </template> </gl-token-selector> </template> |