Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/invite_members/components/import_project_members_modal.vue')
-rw-r--r--app/assets/javascripts/invite_members/components/import_project_members_modal.vue144
1 files changed, 128 insertions, 16 deletions
diff --git a/app/assets/javascripts/invite_members/components/import_project_members_modal.vue b/app/assets/javascripts/invite_members/components/import_project_members_modal.vue
index 66d4a9ccc07..5599ad276f0 100644
--- a/app/assets/javascripts/invite_members/components/import_project_members_modal.vue
+++ b/app/assets/javascripts/invite_members/components/import_project_members_modal.vue
@@ -1,5 +1,5 @@
<script>
-import { GlFormGroup, GlModal, GlSprintf } from '@gitlab/ui';
+import { GlFormGroup, GlModal, GlSprintf, GlAlert, GlCollapse, GlIcon, GlButton } from '@gitlab/ui';
import { uniqueId, isEmpty } from 'lodash';
import { importProjectMembers } from '~/api/projects_api';
import { BV_SHOW_MODAL, BV_HIDE_MODAL } from '~/lib/utils/constants';
@@ -16,8 +16,10 @@ import {
PROJECT_SELECT_LABEL_ID,
IMPORT_PROJECT_MEMBERS_MODAL_TRACKING_CATEGORY,
IMPORT_PROJECT_MEMBERS_MODAL_TRACKING_LABEL,
+ MEMBER_MODAL_LABELS,
} from '../constants';
+import { responseFromSuccess } from '../utils/response_message_parser';
import UserLimitNotification from './user_limit_notification.vue';
import ProjectSelect from './project_select.vue';
@@ -27,6 +29,10 @@ export default {
GlFormGroup,
GlModal,
GlSprintf,
+ GlAlert,
+ GlCollapse,
+ GlIcon,
+ GlButton,
UserLimitNotification,
ProjectSelect,
},
@@ -60,6 +66,9 @@ export default {
return {
projectToBeImported: {},
invalidFeedbackMessage: '',
+ totalMembersCount: 0,
+ invalidMembers: {},
+ isErrorsSectionExpanded: false,
isLoading: false,
};
},
@@ -94,6 +103,40 @@ export default {
actionCancel() {
return { text: this.$options.i18n.modalCancelButton };
},
+ hasInvalidMembers() {
+ return !isEmpty(this.invalidMembers);
+ },
+ memberErrorTitle() {
+ return sprintf(
+ s__(
+ 'InviteMembersModal|The following %{errorMembersLength} out of %{totalMembersCount} members could not be added',
+ ),
+ { errorMembersLength: this.errorList.length, totalMembersCount: this.totalMembersCount },
+ );
+ },
+ errorList() {
+ return Object.entries(this.invalidMembers).map(([member, error]) => {
+ return { member, displayedMemberName: `@${member}`, message: error };
+ });
+ },
+ errorsLimited() {
+ return this.errorList.slice(0, this.$options.errorsLimit);
+ },
+ errorsExpanded() {
+ return this.errorList.slice(this.$options.errorsLimit);
+ },
+ shouldErrorsSectionExpand() {
+ return Boolean(this.errorsExpanded.length);
+ },
+ errorCollapseText() {
+ if (this.isErrorsSectionExpanded) {
+ return this.$options.labels.expandedErrors;
+ }
+
+ return sprintf(this.$options.labels.collapsedErrors, {
+ count: this.errorsExpanded.length,
+ });
+ },
},
mounted() {
if (this.reloadPageOnSubmit) {
@@ -113,21 +156,37 @@ export default {
this.$root.$emit(BV_HIDE_MODAL, this.$options.modalId);
},
resetFields() {
+ this.clearValidation();
this.invalidFeedbackMessage = '';
this.projectToBeImported = {};
},
- submitImport(e) {
+ async submitImport(event) {
// We never want to hide when submitting
- e.preventDefault();
+ event.preventDefault();
this.isLoading = true;
- return importProjectMembers(this.projectId, this.projectToBeImported.id)
- .then(this.onInviteSuccess)
- .catch(this.showErrorAlert)
- .finally(() => {
- this.isLoading = false;
- this.projectToBeImported = {};
- });
+
+ try {
+ const response = await importProjectMembers(this.projectId, this.projectToBeImported.id);
+
+ const { error, message } = responseFromSuccess(response);
+
+ if (error) {
+ this.totalMembersCount = response.data.total_members_count;
+ this.showMemberErrors(message);
+ } else {
+ this.onInviteSuccess();
+ }
+ } catch {
+ this.showErrorAlert();
+ } finally {
+ this.isLoading = false;
+ this.projectToBeImported = {};
+ }
+ },
+ showMemberErrors(message) {
+ this.invalidMembers = message;
+ this.$refs.alerts.focus();
},
onInviteSuccess() {
this.track('invite_successful');
@@ -151,6 +210,13 @@ export default {
onClose() {
this.track('click_x');
},
+ clearValidation() {
+ this.invalidFeedbackMessage = '';
+ this.invalidMembers = {};
+ },
+ toggleErrorExpansion() {
+ this.isErrorsSectionExpanded = !this.isErrorsSectionExpanded;
+ },
},
toastOptions() {
return {
@@ -173,8 +239,10 @@ export default {
defaultError: s__('ImportAProjectModal|Unable to import project members'),
successMessage: s__('ImportAProjectModal|Successfully imported'),
},
+ errorsLimit: 2,
projectSelectLabelId: PROJECT_SELECT_LABEL_ID,
modalId: uniqueId('import-a-project-modal-'),
+ labels: MEMBER_MODAL_LABELS,
};
</script>
@@ -186,18 +254,62 @@ export default {
:title="$options.i18n.modalTitle"
:action-primary="actionPrimary"
:action-cancel="actionCancel"
+ data-testid="import-project-members-modal"
no-focus-on-show
@primary="submitImport"
@hidden="resetFields"
@cancel="onCancel"
@close="onClose"
>
- <user-limit-notification
- v-if="showUserLimitNotification"
- class="gl-mb-5"
- :limit-variant="limitVariant"
- :users-limit-dataset="usersLimitDataset"
- />
+ <div ref="alerts" tabindex="-1">
+ <gl-alert
+ v-if="hasInvalidMembers"
+ class="gl-mb-4"
+ variant="danger"
+ :dismissible="false"
+ :title="memberErrorTitle"
+ data-testid="alert-member-error"
+ >
+ {{ $options.labels.memberErrorListText }}
+ <ul class="gl-pl-5 gl-mb-0">
+ <li v-for="error in errorsLimited" :key="error.member" data-testid="errors-limited-item">
+ <strong>{{ error.displayedMemberName }}:</strong> {{ error.message }}
+ </li>
+ </ul>
+ <template v-if="shouldErrorsSectionExpand">
+ <gl-collapse v-model="isErrorsSectionExpanded">
+ <ul class="gl-pl-5 gl-mb-0">
+ <li
+ v-for="error in errorsExpanded"
+ :key="error.member"
+ data-testid="errors-expanded-item"
+ >
+ <strong>{{ error.displayedMemberName }}:</strong> {{ error.message }}
+ </li>
+ </ul>
+ </gl-collapse>
+ <gl-button
+ class="gl-text-decoration-none! gl-shadow-none! gl-mt-3"
+ data-testid="accordion-button"
+ variant="link"
+ @click="toggleErrorExpansion"
+ >
+ {{ errorCollapseText }}
+ <gl-icon
+ name="chevron-down"
+ class="gl-transition-medium"
+ :class="{ 'gl-rotate-180': isErrorsSectionExpanded }"
+ />
+ </gl-button>
+ </template>
+ </gl-alert>
+ <user-limit-notification
+ v-else-if="showUserLimitNotification"
+ class="gl-mb-5"
+ :limit-variant="limitVariant"
+ :users-limit-dataset="usersLimitDataset"
+ />
+ </div>
<p ref="modalIntro">
<gl-sprintf :message="modalIntro">
<template #strong="{ content }">