summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/jira_connect/subscriptions/components/groups_list.vue
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/jira_connect/subscriptions/components/groups_list.vue')
-rw-r--r--app/assets/javascripts/jira_connect/subscriptions/components/groups_list.vue132
1 files changed, 132 insertions, 0 deletions
diff --git a/app/assets/javascripts/jira_connect/subscriptions/components/groups_list.vue b/app/assets/javascripts/jira_connect/subscriptions/components/groups_list.vue
new file mode 100644
index 00000000000..5a49d7c1a90
--- /dev/null
+++ b/app/assets/javascripts/jira_connect/subscriptions/components/groups_list.vue
@@ -0,0 +1,132 @@
+<script>
+import { GlLoadingIcon, GlPagination, GlAlert, GlSearchBoxByType } from '@gitlab/ui';
+import { fetchGroups } from '~/jira_connect/subscriptions/api';
+import {
+ DEFAULT_GROUPS_PER_PAGE,
+ MINIMUM_SEARCH_TERM_LENGTH,
+} from '~/jira_connect/subscriptions/constants';
+import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils';
+import { s__ } from '~/locale';
+import GroupsListItem from './groups_list_item.vue';
+
+export default {
+ components: {
+ GlLoadingIcon,
+ GlPagination,
+ GlAlert,
+ GlSearchBoxByType,
+ GroupsListItem,
+ },
+ inject: {
+ groupsPath: {
+ default: '',
+ },
+ },
+ data() {
+ return {
+ groups: [],
+ isLoadingInitial: true,
+ isLoadingMore: false,
+ page: 1,
+ totalItems: 0,
+ errorMessage: null,
+ searchTerm: '',
+ };
+ },
+ computed: {
+ showPagination() {
+ return this.totalItems > this.$options.DEFAULT_GROUPS_PER_PAGE && this.groups.length > 0;
+ },
+ },
+ mounted() {
+ return this.loadGroups().finally(() => {
+ this.isLoadingInitial = false;
+ });
+ },
+ methods: {
+ loadGroups() {
+ // fetchGroups returns no results for search terms 0 < {length} < 3.
+ // The desired UX is to return the unfiltered results for searches {length} < 3.
+ // Here, we set the search to an empty string if {length} < 3
+ const search = this.searchTerm?.length < MINIMUM_SEARCH_TERM_LENGTH ? '' : this.searchTerm;
+
+ this.isLoadingMore = true;
+ return fetchGroups(this.groupsPath, {
+ page: this.page,
+ perPage: this.$options.DEFAULT_GROUPS_PER_PAGE,
+ search,
+ })
+ .then((response) => {
+ const { page, total } = parseIntPagination(normalizeHeaders(response.headers));
+ this.page = page;
+ this.totalItems = total;
+ this.groups = response.data;
+ })
+ .catch(() => {
+ this.errorMessage = s__('Integrations|Failed to load namespaces. Please try again.');
+ })
+ .finally(() => {
+ this.isLoadingMore = false;
+ });
+ },
+ onGroupSearch(searchTerm) {
+ // keep a copy of the search term for pagination
+ this.searchTerm = searchTerm;
+ // reset the current page
+ this.page = 1;
+ return this.loadGroups();
+ },
+ },
+ DEFAULT_GROUPS_PER_PAGE,
+};
+</script>
+
+<template>
+ <div>
+ <gl-alert v-if="errorMessage" class="gl-mb-5" variant="danger" @dismiss="errorMessage = null">
+ {{ errorMessage }}
+ </gl-alert>
+
+ <gl-search-box-by-type
+ class="gl-mb-5"
+ debounce="500"
+ :placeholder="__('Search by name')"
+ :is-loading="isLoadingMore"
+ :value="searchTerm"
+ @input="onGroupSearch"
+ />
+
+ <gl-loading-icon v-if="isLoadingInitial" size="md" />
+ <div v-else-if="groups.length === 0" class="gl-text-center">
+ <h5>{{ s__('Integrations|No available namespaces.') }}</h5>
+ <p class="gl-mt-5">
+ {{ s__('Integrations|You must have owner or maintainer permissions to link namespaces.') }}
+ </p>
+ </div>
+ <ul
+ v-else
+ class="gl-list-style-none gl-pl-0 gl-border-t-1 gl-border-t-solid gl-border-t-gray-100"
+ :class="{ 'gl-opacity-5': isLoadingMore }"
+ data-testid="groups-list"
+ >
+ <groups-list-item
+ v-for="group in groups"
+ :key="group.id"
+ :group="group"
+ :disabled="isLoadingMore"
+ @error="errorMessage = $event"
+ />
+ </ul>
+
+ <div class="gl-display-flex gl-justify-content-center gl-mt-5">
+ <gl-pagination
+ v-if="showPagination"
+ v-model="page"
+ class="gl-mb-0"
+ :per-page="$options.DEFAULT_GROUPS_PER_PAGE"
+ :total-items="totalItems"
+ @input="loadGroups"
+ />
+ </div>
+ </div>
+</template>