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/boards/components')
-rw-r--r--app/assets/javascripts/boards/components/board_app.vue14
-rw-r--r--app/assets/javascripts/boards/components/board_card_inner.vue22
-rw-r--r--app/assets/javascripts/boards/components/board_content.vue12
-rw-r--r--app/assets/javascripts/boards/components/board_form.vue10
-rw-r--r--app/assets/javascripts/boards/components/board_list.vue82
-rw-r--r--app/assets/javascripts/boards/components/board_list_header.vue102
-rw-r--r--app/assets/javascripts/boards/components/board_new_issue.vue83
-rw-r--r--app/assets/javascripts/boards/components/issue_board_filtered_search.vue2
-rw-r--r--app/assets/javascripts/boards/components/project_select.vue119
9 files changed, 306 insertions, 140 deletions
diff --git a/app/assets/javascripts/boards/components/board_app.vue b/app/assets/javascripts/boards/components/board_app.vue
index 0b9243c07c5..ca8299ddf80 100644
--- a/app/assets/javascripts/boards/components/board_app.vue
+++ b/app/assets/javascripts/boards/components/board_app.vue
@@ -5,9 +5,11 @@ import { s__ } from '~/locale';
import BoardContent from '~/boards/components/board_content.vue';
import BoardSettingsSidebar from '~/boards/components/board_settings_sidebar.vue';
import BoardTopBar from '~/boards/components/board_top_bar.vue';
+import eventHub from '~/boards/eventhub';
import { listsQuery } from 'ee_else_ce/boards/constants';
import { formatBoardLists } from 'ee_else_ce/boards/boards_util';
import activeBoardItemQuery from 'ee_else_ce/boards/graphql/client/active_board_item.query.graphql';
+import errorQuery from '../graphql/client/error.query.graphql';
export default {
i18n: {
@@ -38,6 +40,7 @@ export default {
addColumnFormVisible: false,
isShowingEpicsSwimlanes: Boolean(queryToObject(window.location.search).group_by),
apolloError: null,
+ error: null,
};
},
apollo: {
@@ -75,6 +78,10 @@ export default {
this.apolloError = this.$options.i18n.fetchError;
},
},
+ error: {
+ query: errorQuery,
+ update: (data) => data.boardsAppError,
+ },
},
computed: {
@@ -106,11 +113,16 @@ export default {
},
created() {
window.addEventListener('popstate', refreshCurrentPage);
+ eventHub.$on('updateBoard', this.refetchLists);
},
destroyed() {
window.removeEventListener('popstate', refreshCurrentPage);
+ eventHub.$off('updateBoard', this.refetchLists);
},
methods: {
+ refetchLists() {
+ this.$apollo.queries.boardListsApollo.refetch();
+ },
setActiveId(id) {
this.activeListId = id;
},
@@ -145,7 +157,7 @@ export default {
:is-swimlanes-on="isSwimlanesOn"
:filter-params="filterParams"
:board-lists-apollo="boardListsApollo"
- :apollo-error="apolloError"
+ :apollo-error="apolloError || error"
:list-query-variables="listQueryVariables"
@setActiveList="setActiveId"
@setAddColumnFormVisibility="addColumnFormVisible = $event"
diff --git a/app/assets/javascripts/boards/components/board_card_inner.vue b/app/assets/javascripts/boards/components/board_card_inner.vue
index befd04c29ae..6036f0c359c 100644
--- a/app/assets/javascripts/boards/components/board_card_inner.vue
+++ b/app/assets/javascripts/boards/components/board_card_inner.vue
@@ -43,7 +43,14 @@ export default {
GlTooltip: GlTooltipDirective,
},
mixins: [boardCardInner],
- inject: ['rootPath', 'scopedLabelsAvailable', 'isEpicBoard', 'issuableType', 'isGroupBoard'],
+ inject: [
+ 'rootPath',
+ 'scopedLabelsAvailable',
+ 'isEpicBoard',
+ 'issuableType',
+ 'isGroupBoard',
+ 'isApolloBoard',
+ ],
props: {
item: {
type: Object,
@@ -78,6 +85,9 @@ export default {
},
computed: {
...mapState(['isShowingLabels', 'allowSubEpics']),
+ isLoading() {
+ return this.item.isLoading || this.item.iid === '-1';
+ },
cappedAssignees() {
// e.g. maxRender is 4,
// Render up to all 4 assignees if there are only 4 assigness
@@ -201,7 +211,9 @@ export default {
updateHistory({
url: `${filterPath}${filter}`,
});
- this.performSearch();
+ if (!this.isApolloBoard) {
+ this.performSearch();
+ }
eventHub.$emit('updateTokens');
}
},
@@ -243,7 +255,7 @@ export default {
<a
:href="item.path || item.webUrl || ''"
:title="item.title"
- :class="{ 'gl-text-gray-400!': item.isLoading }"
+ :class="{ 'gl-text-gray-400!': isLoading }"
class="js-no-trigger gl-text-body gl-hover-text-gray-900"
@mousemove.stop
>{{ item.title }}</a
@@ -272,9 +284,9 @@ export default {
<div
class="gl-display-flex align-items-start flex-wrap-reverse board-card-number-container gl-overflow-hidden"
>
- <gl-loading-icon v-if="item.isLoading" size="lg" class="gl-mt-5" />
+ <gl-loading-icon v-if="isLoading" size="lg" class="gl-mt-5" />
<span
- v-if="item.referencePath"
+ v-if="item.referencePath && !isLoading"
class="board-card-number gl-overflow-hidden gl-display-flex gl-mr-3 gl-mt-3 gl-font-sm gl-text-secondary"
:class="{ 'gl-font-base': isEpicBoard }"
>
diff --git a/app/assets/javascripts/boards/components/board_content.vue b/app/assets/javascripts/boards/components/board_content.vue
index a51e4ddc8f8..14c781f588f 100644
--- a/app/assets/javascripts/boards/components/board_content.vue
+++ b/app/assets/javascripts/boards/components/board_content.vue
@@ -4,7 +4,6 @@ import { sortBy } from 'lodash';
import produce from 'immer';
import Draggable from 'vuedraggable';
import { mapState, mapActions } from 'vuex';
-import eventHub from '~/boards/eventhub';
import BoardAddNewColumn from 'ee_else_ce/boards/components/board_add_new_column.vue';
import { defaultSortableOptions } from '~/sortable/constants';
import {
@@ -107,24 +106,15 @@ export default {
return this.canDragColumns ? options : {};
},
errorToDisplay() {
- return this.isApolloBoard ? this.apolloError : this.error;
+ return this.apolloError || this.error || null;
},
},
- created() {
- eventHub.$on('updateBoard', this.refetchLists);
- },
- beforeDestroy() {
- eventHub.$off('updateBoard', this.refetchLists);
- },
methods: {
...mapActions(['moveList', 'unsetError']),
afterFormEnters() {
const el = this.canDragColumns ? this.$refs.list.$el : this.$refs.list;
el.scrollTo({ left: el.scrollWidth, behavior: 'smooth' });
},
- refetchLists() {
- this.$apollo.queries.boardListsApollo.refetch();
- },
highlightList(listId) {
this.highlightedLists.push(listId);
diff --git a/app/assets/javascripts/boards/components/board_form.vue b/app/assets/javascripts/boards/components/board_form.vue
index 604e71f5993..9ea801dc9a2 100644
--- a/app/assets/javascripts/boards/components/board_form.vue
+++ b/app/assets/javascripts/boards/components/board_form.vue
@@ -226,10 +226,12 @@ export default {
}
this.cancel();
- const param = getParameterByName('group_by')
- ? `?group_by=${getParameterByName('group_by')}`
- : '';
- updateHistory({ url: `${this.boardBaseUrl}/${getIdFromGraphQLId(board.id)}${param}` });
+ if (!this.isApolloBoard) {
+ const param = getParameterByName('group_by')
+ ? `?group_by=${getParameterByName('group_by')}`
+ : '';
+ updateHistory({ url: `${this.boardBaseUrl}/${getIdFromGraphQLId(board.id)}${param}` });
+ }
} catch {
this.setError({ message: this.$options.i18n.saveErrorMessage });
} finally {
diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue
index af309ba9912..b4249c63b4d 100644
--- a/app/assets/javascripts/boards/components/board_list.vue
+++ b/app/assets/javascripts/boards/components/board_list.vue
@@ -22,6 +22,7 @@ import {
removeItemFromList,
updateEpicsCount,
updateIssueCountAndWeight,
+ setError,
} from '../graphql/cache_updates';
import { shouldCloneCard, moveItemVariables } from '../boards_util';
import eventHub from '../eventhub';
@@ -33,7 +34,7 @@ export default {
name: 'BoardList',
i18n: {
loading: __('Loading'),
- loadingMoreboardItems: __('Loading more'),
+ loadingMoreBoardItems: __('Loading more'),
showingAllIssues: __('Showing all issues'),
showingAllEpics: __('Showing all epics'),
},
@@ -83,6 +84,7 @@ export default {
isLoadingMore: false,
toListId: null,
toList: {},
+ addItemToListInProgress: false,
};
},
apollo: {
@@ -213,7 +215,8 @@ export default {
return !this.disabled;
},
treeRootWrapper() {
- return this.canMoveIssue && !this.listsFlags[this.list.id]?.addItemToListInProgress
+ return this.canMoveIssue &&
+ (!this.listsFlags[this.list.id]?.addItemToListInProgress || this.addItemToListInProgress)
? Draggable
: 'ul';
},
@@ -468,14 +471,14 @@ export default {
this.updateCountAndWeight({ fromListId, toListId, issuable, cache });
},
- updateCountAndWeight({ fromListId, toListId, issuable, isAddingIssue, cache }) {
+ updateCountAndWeight({ fromListId, toListId, issuable, isAddingItem, cache }) {
if (!this.isEpicBoard) {
updateIssueCountAndWeight({
fromListId,
toListId,
filterParams: this.filterParams,
issuable,
- shouldClone: isAddingIssue || this.shouldCloneCard,
+ shouldClone: isAddingItem || this.shouldCloneCard,
cache,
});
} else {
@@ -486,7 +489,7 @@ export default {
fromListId,
filterParams,
issuable,
- shouldClone: this.shouldCloneCard,
+ shouldClone: isAddingItem || this.shouldCloneCard,
cache,
});
}
@@ -538,6 +541,59 @@ export default {
},
});
},
+ async addListItem(input) {
+ this.toggleForm();
+ this.addItemToListInProgress = true;
+ try {
+ await this.$apollo.mutate({
+ mutation: listIssuablesQueries[this.issuableType].createMutation,
+ variables: {
+ input: this.isEpicBoard ? input : { ...input, moveAfterId: this.boardListItems[0]?.id },
+ withColor: this.isEpicBoard && this.glFeatures.epicColorHighlight,
+ },
+ update: (cache, { data: { createIssuable } }) => {
+ const { issuable } = createIssuable;
+ addItemToList({
+ query: listIssuablesQueries[this.issuableType].query,
+ variables: { ...this.listQueryVariables, id: this.currentList.id },
+ issuable,
+ newIndex: 0,
+ boardType: this.boardType,
+ issuableType: this.issuableType,
+ cache,
+ });
+ this.updateCountAndWeight({
+ fromListId: null,
+ toListId: this.list.id,
+ issuable,
+ isAddingItem: true,
+ cache,
+ });
+ },
+ optimisticResponse: {
+ createIssuable: {
+ errors: [],
+ issuable: {
+ ...listIssuablesQueries[this.issuableType].optimisticResponse,
+ title: input.title,
+ },
+ },
+ },
+ });
+ } catch (error) {
+ setError({
+ message: sprintf(
+ __('An error occurred while creating the %{issuableType}. Please try again.'),
+ {
+ issuableType: this.isEpicBoard ? 'epic' : 'issue',
+ },
+ ),
+ error,
+ });
+ } finally {
+ this.addItemToListInProgress = false;
+ }
+ },
},
};
</script>
@@ -556,8 +612,18 @@ export default {
>
<gl-loading-icon size="sm" />
</div>
- <board-new-issue v-if="issueCreateFormVisible" :list="list" />
- <board-new-epic v-if="epicCreateFormVisible" :list="list" />
+ <board-new-issue
+ v-if="issueCreateFormVisible"
+ :list="list"
+ :board-id="boardId"
+ @addNewIssue="addListItem"
+ />
+ <board-new-epic
+ v-if="epicCreateFormVisible"
+ :list="list"
+ :board-id="boardId"
+ @addNewEpic="addListItem"
+ />
<component
:is="treeRootWrapper"
v-show="!loading"
@@ -610,7 +676,7 @@ export default {
<gl-loading-icon
v-if="loadingMore"
size="sm"
- :label="$options.i18n.loadingMoreboardItems"
+ :label="$options.i18n.loadingMoreBoardItems"
/>
<span v-if="showingAllItems">{{ showingAllItemsText }}</span>
<span v-else>{{ paginatedIssueText }}</span>
diff --git a/app/assets/javascripts/boards/components/board_list_header.vue b/app/assets/javascripts/boards/components/board_list_header.vue
index 61a9b22bfc5..8db86d0e894 100644
--- a/app/assets/javascripts/boards/components/board_list_header.vue
+++ b/app/assets/javascripts/boards/components/board_list_header.vue
@@ -1,12 +1,12 @@
<script>
import {
GlButton,
+ GlButtonGroup,
GlLabel,
GlTooltip,
GlIcon,
GlSprintf,
GlTooltipDirective,
- GlDisclosureDropdown,
} from '@gitlab/ui';
import { mapActions, mapState } from 'vuex';
import { isListDraggable } from '~/boards/boards_util';
@@ -35,15 +35,14 @@ import ItemCount from './item_count.vue';
export default {
i18n: {
newIssue: s__('Boards|Create new issue'),
- listActions: s__('Boards|List actions'),
newEpic: s__('Boards|Create new epic'),
listSettings: s__('Boards|Edit list settings'),
expand: s__('Boards|Expand'),
collapse: s__('Boards|Collapse'),
},
components: {
- GlDisclosureDropdown,
GlButton,
+ GlButtonGroup,
GlLabel,
GlTooltip,
GlIcon,
@@ -194,50 +193,6 @@ export default {
canShowTotalWeight() {
return this.weightFeatureAvailable && !this.isLoading;
},
- actionListItems() {
- const items = [];
-
- if (this.isNewIssueShown) {
- const newIssueText = this.$options.i18n.newIssue;
- items.push({
- text: newIssueText,
- action: this.showNewIssueForm,
- extraAttrs: {
- 'data-testid': 'newIssueBtn',
- title: newIssueText,
- 'aria-label': newIssueText,
- },
- });
- }
-
- if (this.isNewEpicShown) {
- const newEpicText = this.$options.i18n.newEpic;
- items.push({
- text: newEpicText,
- action: this.showNewEpicForm,
- extraAttrs: {
- 'data-testid': 'newEpicBtn',
- title: newEpicText,
- 'aria-label': newEpicText,
- },
- });
- }
-
- if (this.isSettingsShown) {
- const listSettingsText = this.$options.i18n.listSettings;
- items.push({
- text: listSettingsText,
- action: this.openSidebarSettings,
- extraAttrs: {
- 'data-testid': 'settingsBtn',
- title: listSettingsText,
- 'aria-label': listSettingsText,
- },
- });
- }
-
- return items;
- },
},
apollo: {
boardList: {
@@ -525,23 +480,42 @@ export default {
<!-- EE end -->
</span>
</div>
- <gl-disclosure-dropdown
- v-if="showListHeaderActions"
- v-gl-tooltip.hover.top="{
- title: $options.i18n.listActions,
- boundary: 'viewport',
- }"
- data-testid="header-list-actions"
- class="gl-py-2 gl-ml-3"
- :aria-label="$options.i18n.listActions"
- :title="$options.i18n.listActions"
- category="tertiary"
- icon="ellipsis_v"
- :text-sr-only="true"
- :items="actionListItems"
- no-caret
- placement="right"
- />
+ <gl-button-group v-if="showListHeaderActions" class="board-list-button-group gl-pl-2">
+ <gl-button
+ v-if="isNewIssueShown"
+ ref="newIssueBtn"
+ v-gl-tooltip.hover
+ :aria-label="$options.i18n.newIssue"
+ :title="$options.i18n.newIssue"
+ size="small"
+ icon="plus"
+ data-testid="new-issue-btn"
+ @click="showNewIssueForm"
+ />
+
+ <gl-button
+ v-if="isNewEpicShown"
+ v-gl-tooltip.hover
+ :aria-label="$options.i18n.newEpic"
+ :title="$options.i18n.newEpic"
+ size="small"
+ icon="plus"
+ data-testid="new-epic-btn"
+ @click="showNewEpicForm"
+ />
+
+ <gl-button
+ v-if="isSettingsShown"
+ ref="settingsBtn"
+ v-gl-tooltip.hover
+ :aria-label="$options.i18n.listSettings"
+ size="small"
+ :title="$options.i18n.listSettings"
+ icon="settings"
+ data-testid="settings-btn"
+ @click="openSidebarSettings"
+ />
+ </gl-button-group>
</h3>
</header>
</template>
diff --git a/app/assets/javascripts/boards/components/board_new_issue.vue b/app/assets/javascripts/boards/components/board_new_issue.vue
index 8b9fafca306..b68444fb011 100644
--- a/app/assets/javascripts/boards/components/board_new_issue.vue
+++ b/app/assets/javascripts/boards/components/board_new_issue.vue
@@ -1,30 +1,73 @@
<script>
-import { mapActions, mapGetters, mapState } from 'vuex';
-import { getMilestone } from 'ee_else_ce/boards/boards_util';
+import { mapActions, mapGetters } from 'vuex';
+import { s__ } from '~/locale';
+import { getMilestone, formatIssueInput, getBoardQuery } from 'ee_else_ce/boards/boards_util';
import BoardNewIssueMixin from 'ee_else_ce/boards/mixins/board_new_issue';
import { toggleFormEventPrefix } from '../constants';
import eventHub from '../eventhub';
+import { setError } from '../graphql/cache_updates';
import BoardNewItem from './board_new_item.vue';
import ProjectSelect from './project_select.vue';
export default {
name: 'BoardNewIssue',
+ i18n: {
+ errorFetchingBoard: s__('Boards|An error occurred while fetching board. Please try again.'),
+ },
components: {
BoardNewItem,
ProjectSelect,
},
mixins: [BoardNewIssueMixin],
- inject: ['groupId', 'fullPath', 'isGroupBoard'],
+ inject: ['boardType', 'groupId', 'fullPath', 'isGroupBoard', 'isEpicBoard', 'isApolloBoard'],
props: {
list: {
type: Object,
required: true,
},
+ boardId: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ selectedProject: {},
+ board: {},
+ };
+ },
+ apollo: {
+ board: {
+ query() {
+ return getBoardQuery(this.boardType, this.isEpicBoard);
+ },
+ variables() {
+ return {
+ fullPath: this.fullPath,
+ boardId: this.boardId,
+ };
+ },
+ skip() {
+ return !this.isApolloBoard;
+ },
+ update(data) {
+ const { board } = data.workspace;
+ return {
+ ...board,
+ labels: board.labels?.nodes,
+ };
+ },
+ error(error) {
+ setError({
+ error,
+ message: this.$options.i18n.errorFetchingBoard,
+ });
+ },
+ },
},
computed: {
- ...mapState(['selectedProject']),
...mapGetters(['getBoardItemsByList']),
formEventPrefix() {
return toggleFormEventPrefix.issue;
@@ -42,8 +85,20 @@ export default {
const labels = this.list.label ? [this.list.label] : [];
const assignees = this.list.assignee ? [this.list.assignee] : [];
const milestone = getMilestone(this.list);
- const firstItemId = this.getBoardItemsByList(this.list.id)[0]?.id;
+ if (this.isApolloBoard) {
+ return this.addNewIssueToList({
+ issueInput: {
+ title,
+ labelIds: labels?.map((l) => l.id),
+ assigneeIds: assignees?.map((a) => a?.id),
+ milestoneId: milestone?.id,
+ projectPath: this.projectPath,
+ },
+ });
+ }
+
+ const firstItemId = this.getBoardItemsByList(this.list.id)[0]?.id;
return this.addListNewIssue({
list: this.list,
issueInput: {
@@ -58,6 +113,22 @@ export default {
this.cancel();
});
},
+ addNewIssueToList({ issueInput }) {
+ const { labels, assignee, milestone, weight } = this.board;
+ const config = {
+ labels,
+ assigneeId: assignee?.id || null,
+ milestoneId: milestone?.id || null,
+ weight,
+ };
+ const input = formatIssueInput(issueInput, config);
+
+ if (!this.isGroupBoard) {
+ input.projectPath = this.fullPath;
+ }
+
+ this.$emit('addNewIssue', input);
+ },
cancel() {
eventHub.$emit(`${this.formEventPrefix}${this.list.id}`);
},
@@ -74,6 +145,6 @@ export default {
@form-submit="submit"
@form-cancel="cancel"
>
- <project-select v-if="isGroupBoard" :group-id="groupId" :list="list" />
+ <project-select v-if="isGroupBoard" v-model="selectedProject" :list="list" />
</board-new-item>
</template>
diff --git a/app/assets/javascripts/boards/components/issue_board_filtered_search.vue b/app/assets/javascripts/boards/components/issue_board_filtered_search.vue
index 3c056f296e1..f60f00be368 100644
--- a/app/assets/javascripts/boards/components/issue_board_filtered_search.vue
+++ b/app/assets/javascripts/boards/components/issue_board_filtered_search.vue
@@ -75,6 +75,7 @@ export default {
type: TOKEN_TYPE_ASSIGNEE,
operators: OPERATORS_IS_NOT,
token: UserToken,
+ dataType: 'user',
unique: true,
fetchUsers,
preloadedUsers: this.preloadedUsers(),
@@ -86,6 +87,7 @@ export default {
operators: OPERATORS_IS_NOT,
symbol: '@',
token: UserToken,
+ dataType: 'user',
unique: true,
fetchUsers,
preloadedUsers: this.preloadedUsers(),
diff --git a/app/assets/javascripts/boards/components/project_select.vue b/app/assets/javascripts/boards/components/project_select.vue
index 960c8e472b8..7bbc444701a 100644
--- a/app/assets/javascripts/boards/components/project_select.vue
+++ b/app/assets/javascripts/boards/components/project_select.vue
@@ -1,11 +1,8 @@
<script>
import { GlCollapsibleListbox } from '@gitlab/ui';
-import { mapActions, mapGetters, mapState } from 'vuex';
-import { debounce } from 'lodash';
import { s__ } from '~/locale';
-import { featureAccessLevel } from '~/pages/projects/shared/permissions/constants';
-import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
-import { ListType } from '../constants';
+import groupProjectsQuery from '../graphql/group_projects.query.graphql';
+import { setError } from '../graphql/cache_updates';
export default {
name: 'ProjectSelect',
@@ -14,6 +11,9 @@ export default {
dropdownText: s__(`BoardNewIssue|Select a project`),
searchPlaceholder: s__(`BoardNewIssue|Search projects`),
emptySearchResult: s__(`BoardNewIssue|No matching results`),
+ errorFetchingProjects: s__(
+ 'Boards|An error occurred while fetching group projects. Please try again.',
+ ),
},
defaultFetchOptions: {
with_issues_enabled: true,
@@ -24,70 +24,107 @@ export default {
components: {
GlCollapsibleListbox,
},
- inject: ['groupId'],
+ inject: ['groupId', 'fullPath'],
+ model: {
+ prop: 'selectedProject',
+ event: 'selectProject',
+ },
props: {
list: {
type: Object,
required: true,
},
+ selectedProject: {
+ type: Object,
+ required: true,
+ },
},
data() {
return {
initialLoading: true,
selectedProjectId: '',
- selectedProject: {},
searchTerm: '',
+ projects: {},
+ isLoadingMore: false,
};
},
+ apollo: {
+ projects: {
+ query: groupProjectsQuery,
+ variables() {
+ return {
+ fullPath: this.fullPath,
+ search: this.searchTerm,
+ };
+ },
+ update(data) {
+ return data.group.projects;
+ },
+ error(error) {
+ setError({
+ error,
+ message: this.$options.i18n.errorFetchingProjects,
+ });
+ },
+ result() {
+ this.initialLoading = false;
+ },
+ },
+ },
computed: {
- ...mapState(['groupProjectsFlags']),
- ...mapGetters(['activeGroupProjects']),
- projects() {
- return this.activeGroupProjects.map((project) => ({
- value: project.id,
- text: project.nameWithNamespace,
- }));
+ isLoading() {
+ return this.$apollo.queries.projects.loading && !this.isLoadingMore;
+ },
+ activeGroupProjects() {
+ return (
+ this.projects?.nodes?.map((project) => ({
+ value: project.id,
+ text: project.nameWithNamespace,
+ })) || []
+ );
},
selectedProjectName() {
return this.selectedProject.name || this.$options.i18n.dropdownText;
},
- fetchOptions() {
- const additionalAttrs = {};
- if (this.list.type && this.list.type !== ListType.backlog) {
- additionalAttrs.min_access_level = featureAccessLevel.EVERYONE;
- }
-
- return {
- ...this.$options.defaultFetchOptions,
- ...additionalAttrs,
- };
- },
isFetchResultEmpty() {
return this.activeGroupProjects.length === 0;
},
hasNextPage() {
- return this.groupProjectsFlags.pageInfo?.hasNextPage;
+ return this.projects.pageInfo?.hasNextPage;
},
},
watch: {
- searchTerm: debounce(function debouncedSearch() {
- this.fetchGroupProjects({ search: this.searchTerm });
- }, DEFAULT_DEBOUNCE_AND_THROTTLE_MS),
- },
- mounted() {
- this.fetchGroupProjects({});
- this.initialLoading = false;
+ endCursor() {
+ return this.projects.pageInfo?.endCursor;
+ },
},
methods: {
- ...mapActions(['fetchGroupProjects', 'setSelectedProject']),
selectProject(projectId) {
this.selectedProjectId = projectId;
- this.selectedProject = this.activeGroupProjects.find((project) => project.id === projectId);
- this.setSelectedProject(this.selectedProject);
+ this.$emit(
+ 'selectProject',
+ this.projects.nodes.find((project) => project.id === projectId),
+ );
},
- loadMoreProjects() {
+ async loadMoreProjects() {
if (!this.hasNextPage) return;
- this.fetchGroupProjects({ search: this.searchTerm, fetchNext: true });
+ this.isLoadingMore = true;
+ try {
+ await this.$apollo.queries.projects.fetchMore({
+ variables: {
+ fullPath: this.fullPath,
+ search: this.searchTerm,
+ after: this.endCursor,
+ },
+ });
+ } catch (error) {
+ setError({
+ error,
+ message: this.$options.i18n.errorFetchingProjects,
+ });
+ } finally {
+ this.isLoadingMore = false;
+ }
},
onSearch(query) {
this.searchTerm = query;
@@ -107,14 +144,14 @@ export default {
searchable
infinite-scroll
data-testid="project-select-dropdown"
- :items="projects"
+ :items="activeGroupProjects"
:toggle-text="selectedProjectName"
:header-text="$options.i18n.headerTitle"
:loading="initialLoading"
- :searching="groupProjectsFlags.isLoading"
+ :searching="isLoading"
:search-placeholder="$options.i18n.searchPlaceholder"
:no-results-text="$options.i18n.emptySearchResult"
- :infinite-scroll-loading="groupProjectsFlags.isLoadingMore"
+ :infinite-scroll-loading="isLoadingMore"
@select="selectProject"
@search="onSearch"
@bottom-reached="loadMoreProjects"