diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-07-04 12:11:02 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-07-04 12:11:02 +0300 |
commit | 5f95234f7babb69685710dbfc637f29eeac2a917 (patch) | |
tree | 703240ac11f4565a81c505aa10821fbbd506d888 /app/assets/javascripts/boards | |
parent | 18160560396d57c07a9525eb033e66afb1f07674 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/boards')
6 files changed, 180 insertions, 18 deletions
diff --git a/app/assets/javascripts/boards/components/board_card_inner.vue b/app/assets/javascripts/boards/components/board_card_inner.vue index befd04c29ae..dc72936fd33 100644 --- a/app/assets/javascripts/boards/components/board_card_inner.vue +++ b/app/assets/javascripts/boards/components/board_card_inner.vue @@ -78,6 +78,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 @@ -243,7 +246,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 +275,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_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_new_issue.vue b/app/assets/javascripts/boards/components/board_new_issue.vue index 54fa4afcd78..b68444fb011 100644 --- a/app/assets/javascripts/boards/components/board_new_issue.vue +++ b/app/assets/javascripts/boards/components/board_new_issue.vue @@ -1,33 +1,72 @@ <script> import { mapActions, mapGetters } from 'vuex'; -import { getMilestone } from 'ee_else_ce/boards/boards_util'; +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: ['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: { ...mapGetters(['getBoardItemsByList']), formEventPrefix() { @@ -46,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: { @@ -62,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}`); }, diff --git a/app/assets/javascripts/boards/constants.js b/app/assets/javascripts/boards/constants.js index d4d1bc7804e..cb607e5220e 100644 --- a/app/assets/javascripts/boards/constants.js +++ b/app/assets/javascripts/boards/constants.js @@ -1,6 +1,7 @@ import boardListsQuery from 'ee_else_ce/boards/graphql/board_lists.query.graphql'; import { TYPE_EPIC, TYPE_ISSUE, WORKSPACE_GROUP, WORKSPACE_PROJECT } from '~/issues/constants'; import { s__, __ } from '~/locale'; +import { TYPENAME_ISSUE } from '~/graphql_shared/constants'; import updateEpicSubscriptionMutation from '~/sidebar/queries/update_epic_subscription.mutation.graphql'; import updateEpicTitleMutation from '~/sidebar/queries/update_epic_title.mutation.graphql'; import createBoardListMutation from './graphql/board_list_create.mutation.graphql'; @@ -11,6 +12,7 @@ import toggleListCollapsedMutation from './graphql/client/board_toggle_collapsed import issueSetSubscriptionMutation from './graphql/issue_set_subscription.mutation.graphql'; import issueSetTitleMutation from './graphql/issue_set_title.mutation.graphql'; import issueMoveListMutation from './graphql/issue_move_list.mutation.graphql'; +import issueCreateMutation from './graphql/issue_create.mutation.graphql'; import groupBoardQuery from './graphql/group_board.query.graphql'; import projectBoardQuery from './graphql/project_board.query.graphql'; import listIssuesQuery from './graphql/lists_issues.query.graphql'; @@ -126,6 +128,30 @@ export const listIssuablesQueries = { [TYPE_ISSUE]: { query: listIssuesQuery, moveMutation: issueMoveListMutation, + createMutation: issueCreateMutation, + optimisticResponse: { + assignees: { nodes: [], __typename: 'UserCoreConnection' }, + confidential: false, + dueDate: null, + emailsDisabled: false, + hidden: false, + humanTimeEstimate: null, + humanTotalTimeSpent: null, + id: 'gid://gitlab/Issue/-1', + iid: '-1', + labels: { nodes: [], __typename: 'LabelConnection' }, + milestone: null, + referencePath: '', + relativePosition: null, + severity: 'UNKNOWN', + timeEstimate: 0, + title: '', + totalTimeSpent: 0, + type: 'ISSUE', + webUrl: '', + weight: null, + __typename: TYPENAME_ISSUE, + }, }, }; diff --git a/app/assets/javascripts/boards/graphql/issue_create.mutation.graphql b/app/assets/javascripts/boards/graphql/issue_create.mutation.graphql index 643d5dcfe4c..55cb34c0930 100644 --- a/app/assets/javascripts/boards/graphql/issue_create.mutation.graphql +++ b/app/assets/javascripts/boards/graphql/issue_create.mutation.graphql @@ -1,8 +1,8 @@ #import "ee_else_ce/boards/graphql/issue.fragment.graphql" mutation CreateIssue($input: CreateIssueInput!) { - createIssue(input: $input) { - issue { + createIssuable: createIssue(input: $input) { + issuable: issue { ...Issue } errors diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js index d96d92948be..e044283534a 100644 --- a/app/assets/javascripts/boards/stores/actions.js +++ b/app/assets/javascripts/boards/stores/actions.js @@ -743,11 +743,11 @@ export default { }, }) .then(({ data }) => { - if (data.createIssue.errors.length) { + if (data.createIssuable.errors.length) { throw new Error(); } - const rawIssue = data.createIssue?.issue; + const rawIssue = data.createIssuable?.issuable; const formattedIssue = formatIssue(rawIssue); dispatch('removeListItem', { listId: list.id, itemId: placeholderId }); dispatch('addListItem', { list, item: formattedIssue, position: 0 }); |