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/members')
-rw-r--r--app/assets/javascripts/members/components/filter_sort/members_filtered_search_bar.vue16
-rw-r--r--app/assets/javascripts/members/components/members_tabs.vue124
-rw-r--r--app/assets/javascripts/members/components/table/members_table.vue32
-rw-r--r--app/assets/javascripts/members/index.js2
-rw-r--r--app/assets/javascripts/members/store/state.js2
-rw-r--r--app/assets/javascripts/members/utils.js18
6 files changed, 174 insertions, 20 deletions
diff --git a/app/assets/javascripts/members/components/filter_sort/members_filtered_search_bar.vue b/app/assets/javascripts/members/components/filter_sort/members_filtered_search_bar.vue
index cc97d235a9c..cc0533391df 100644
--- a/app/assets/javascripts/members/components/filter_sort/members_filtered_search_bar.vue
+++ b/app/assets/javascripts/members/components/filter_sort/members_filtered_search_bar.vue
@@ -1,10 +1,11 @@
<script>
import { GlFilteredSearchToken } from '@gitlab/ui';
import { mapState } from 'vuex';
-import { getParameterByName } from '~/lib/utils/common_utils';
-import { setUrlParams, queryToObject } from '~/lib/utils/url_utility';
+import { getParameterByName, urlParamsToObject } from '~/lib/utils/common_utils';
+import { setUrlParams } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
import { SEARCH_TOKEN_TYPE, SORT_PARAM } from '~/members/constants';
+import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants';
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
export default {
@@ -17,7 +18,7 @@ export default {
title: s__('Members|2FA'),
token: GlFilteredSearchToken,
unique: true,
- operators: [{ value: '=', description: 'is' }],
+ operators: OPERATOR_IS_ONLY,
options: [
{ value: 'enabled', title: s__('Members|Enabled') },
{ value: 'disabled', title: s__('Members|Disabled') },
@@ -30,7 +31,7 @@ export default {
title: s__('Members|Membership'),
token: GlFilteredSearchToken,
unique: true,
- operators: [{ value: '=', description: 'is' }],
+ operators: OPERATOR_IS_ONLY,
options: [
{ value: 'exclude', title: s__('Members|Direct') },
{ value: 'only', title: s__('Members|Inherited') },
@@ -63,7 +64,7 @@ export default {
},
},
created() {
- const query = queryToObject(window.location.search);
+ const query = urlParamsToObject(window.location.search);
const tokens = this.tokens
.filter((token) => query[token.type])
@@ -97,9 +98,12 @@ export default {
if (type === SEARCH_TOKEN_TYPE) {
if (value.data !== '') {
+ const { searchParam } = this.filteredSearchBar;
+ const { [searchParam]: searchParamValue } = accumulator;
+
return {
...accumulator,
- [this.filteredSearchBar.searchParam]: value.data,
+ [searchParam]: searchParamValue ? `${searchParamValue} ${value.data}` : value.data,
};
}
} else {
diff --git a/app/assets/javascripts/members/components/members_tabs.vue b/app/assets/javascripts/members/components/members_tabs.vue
new file mode 100644
index 00000000000..37b9135126d
--- /dev/null
+++ b/app/assets/javascripts/members/components/members_tabs.vue
@@ -0,0 +1,124 @@
+<script>
+import { GlTabs, GlTab, GlBadge } from '@gitlab/ui';
+import { mapState } from 'vuex';
+import { urlParamsToObject } from '~/lib/utils/common_utils';
+import { __ } from '~/locale';
+import { MEMBER_TYPES } from '../constants';
+import MembersApp from './app.vue';
+
+const countComputed = (state, namespace) => state[namespace]?.pagination?.totalItems || 0;
+
+export default {
+ name: 'MembersTabs',
+ tabs: [
+ {
+ namespace: MEMBER_TYPES.user,
+ title: __('Members'),
+ },
+ {
+ namespace: MEMBER_TYPES.group,
+ title: __('Groups'),
+ attrs: { 'data-qa-selector': 'groups_list_tab' },
+ },
+ {
+ namespace: MEMBER_TYPES.invite,
+ title: __('Invited'),
+ canManageMembersPermissionsRequired: true,
+ },
+ {
+ namespace: MEMBER_TYPES.accessRequest,
+ title: __('Access requests'),
+ canManageMembersPermissionsRequired: true,
+ },
+ ],
+ urlParams: [],
+ components: { MembersApp, GlTabs, GlTab, GlBadge },
+ inject: ['canManageMembers'],
+ data() {
+ return {
+ selectedTabIndex: 0,
+ };
+ },
+ computed: {
+ ...mapState({
+ userCount(state) {
+ return countComputed(state, MEMBER_TYPES.user);
+ },
+ groupCount(state) {
+ return countComputed(state, MEMBER_TYPES.group);
+ },
+ inviteCount(state) {
+ return countComputed(state, MEMBER_TYPES.invite);
+ },
+ accessRequestCount(state) {
+ return countComputed(state, MEMBER_TYPES.accessRequest);
+ },
+ }),
+ urlParams() {
+ return Object.keys(urlParamsToObject(window.location.search));
+ },
+ activeTabIndexCalculatedFromUrlParams() {
+ return this.$options.tabs.findIndex(({ namespace }) => {
+ return this.getTabUrlParams(namespace).some((urlParam) =>
+ this.urlParams.includes(urlParam),
+ );
+ });
+ },
+ },
+ created() {
+ if (this.activeTabIndexCalculatedFromUrlParams === -1) {
+ return;
+ }
+
+ this.selectedTabIndex = this.activeTabIndexCalculatedFromUrlParams;
+ },
+ methods: {
+ getTabUrlParams(namespace) {
+ const state = this.$store.state[namespace];
+ const urlParams = [];
+
+ if (state?.pagination?.paramName) {
+ urlParams.push(state.pagination.paramName);
+ }
+
+ if (state?.filteredSearchBar?.searchParam) {
+ urlParams.push(state.filteredSearchBar.searchParam);
+ }
+
+ return urlParams;
+ },
+ getTabCount({ namespace }) {
+ return this[`${namespace}Count`];
+ },
+ showTab(tab, index) {
+ if (tab.namespace === MEMBER_TYPES.user) {
+ return true;
+ }
+
+ const { canManageMembersPermissionsRequired = false } = tab;
+ const tabCanBeShown =
+ this.getTabCount(tab) > 0 || this.activeTabIndexCalculatedFromUrlParams === index;
+
+ if (canManageMembersPermissionsRequired) {
+ return this.canManageMembers && tabCanBeShown;
+ }
+
+ return tabCanBeShown;
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-tabs v-model="selectedTabIndex">
+ <template v-for="(tab, index) in $options.tabs">
+ <gl-tab v-if="showTab(tab, index)" :key="tab.namespace" :title-link-attributes="tab.attrs">
+ <template slot="title">
+ <span>{{ tab.title }}</span>
+ <gl-badge size="sm" class="gl-tab-counter-badge">{{ getTabCount(tab) }}</gl-badge>
+ </template>
+ <members-app :namespace="tab.namespace" />
+ </gl-tab>
+ </template>
+ </gl-tabs>
+</template>
diff --git a/app/assets/javascripts/members/components/table/members_table.vue b/app/assets/javascripts/members/components/table/members_table.vue
index 236aeaef418..09ef98ec411 100644
--- a/app/assets/javascripts/members/components/table/members_table.vue
+++ b/app/assets/javascripts/members/components/table/members_table.vue
@@ -1,8 +1,9 @@
<script>
-import { GlTable, GlBadge } from '@gitlab/ui';
+import { GlTable, GlBadge, GlPagination } from '@gitlab/ui';
import { mapState } from 'vuex';
import MembersTableCell from 'ee_else_ce/members/components/table/members_table_cell.vue';
import { canOverride, canRemove, canResend, canUpdate } from 'ee_else_ce/members/utils';
+import { mergeUrlParams } from '~/lib/utils/url_utility';
import initUserPopovers from '~/user_popovers';
import { FIELDS } from '../../constants';
import RemoveGroupLinkModal from '../modals/remove_group_link_modal.vue';
@@ -19,6 +20,7 @@ export default {
components: {
GlTable,
GlBadge,
+ GlPagination,
MemberAvatar,
CreatedAt,
ExpiresAt,
@@ -43,6 +45,9 @@ export default {
tableAttrs(state) {
return state[this.namespace].tableAttrs;
},
+ pagination(state) {
+ return state[this.namespace].pagination;
+ },
}),
filteredFields() {
return FIELDS.filter(
@@ -59,6 +64,11 @@ export default {
userIsLoggedIn() {
return this.currentUserId !== null;
},
+ showPagination() {
+ const { paramName, currentPage, perPage, totalItems } = this.pagination;
+
+ return paramName && currentPage && perPage && totalItems;
+ },
},
mounted() {
initUserPopovers(this.$el.querySelectorAll('.js-user-link'));
@@ -99,6 +109,11 @@ export default {
...(member?.id && { 'data-testid': `members-table-row-${member.id}` }),
};
},
+ paginationLinkGenerator(page) {
+ const { params = {}, paramName } = this.pagination;
+
+ return mergeUrlParams({ ...params, [paramName]: page }, window.location.href);
+ },
},
};
</script>
@@ -119,6 +134,9 @@ export default {
show-empty
:tbody-tr-attr="tbodyTrAttr"
>
+ <template #head()="{ label }">
+ {{ label }}
+ </template>
<template #cell(account)="{ item: member }">
<members-table-cell #default="{ memberType, isCurrentUser }" :member="member">
<member-avatar
@@ -179,6 +197,18 @@ export default {
<span data-testid="col-actions" class="gl-sr-only">{{ label }}</span>
</template>
</gl-table>
+ <gl-pagination
+ v-if="showPagination"
+ :value="pagination.currentPage"
+ :per-page="pagination.perPage"
+ :total-items="pagination.totalItems"
+ :link-gen="paginationLinkGenerator"
+ :prev-text="__('Prev')"
+ :next-text="__('Next')"
+ :label-next-page="__('Go to next page')"
+ :label-prev-page="__('Go to previous page')"
+ align="center"
+ />
<remove-group-link-modal />
<ldap-override-confirmation-modal />
</div>
diff --git a/app/assets/javascripts/members/index.js b/app/assets/javascripts/members/index.js
index 6376b3fa75a..6c913af8a0f 100644
--- a/app/assets/javascripts/members/index.js
+++ b/app/assets/javascripts/members/index.js
@@ -1,7 +1,7 @@
import { GlToast } from '@gitlab/ui';
import Vue from 'vue';
import Vuex from 'vuex';
-import { parseDataAttributes } from 'ee_else_ce/members/utils';
+import { parseDataAttributes } from '~/members/utils';
import App from './components/app.vue';
import membersStore from './store';
diff --git a/app/assets/javascripts/members/store/state.js b/app/assets/javascripts/members/store/state.js
index 4006b4b501d..5415b1c5f25 100644
--- a/app/assets/javascripts/members/store/state.js
+++ b/app/assets/javascripts/members/store/state.js
@@ -1,5 +1,6 @@
export default ({
members,
+ pagination,
tableFields,
tableAttrs,
tableSortableFields,
@@ -8,6 +9,7 @@ export default ({
filteredSearchBar,
}) => ({
members,
+ pagination,
tableFields,
tableAttrs,
tableSortableFields,
diff --git a/app/assets/javascripts/members/utils.js b/app/assets/javascripts/members/utils.js
index 2bf30dd7b6e..be549b40885 100644
--- a/app/assets/javascripts/members/utils.js
+++ b/app/assets/javascripts/members/utils.js
@@ -1,9 +1,5 @@
import { isUndefined } from 'lodash';
-import {
- getParameterByName,
- convertObjectPropsToCamelCase,
- parseBoolean,
-} from '~/lib/utils/common_utils';
+import { getParameterByName, convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { setUrlParams } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import {
@@ -105,14 +101,12 @@ export const buildSortHref = ({
export const canOverride = () => false;
export const parseDataAttributes = (el) => {
- const { members, sourceId, memberPath, canManageMembers } = el.dataset;
+ const { membersData } = el.dataset;
- return {
- members: convertObjectPropsToCamelCase(JSON.parse(members), { deep: true }),
- sourceId: parseInt(sourceId, 10),
- memberPath,
- canManageMembers: parseBoolean(canManageMembers),
- };
+ return convertObjectPropsToCamelCase(JSON.parse(membersData), {
+ deep: true,
+ ignoreKeyNames: ['params'],
+ });
};
export const baseRequestFormatter = (basePropertyName, accessLevelPropertyName) => ({