summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/boards
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/boards')
-rw-r--r--app/assets/javascripts/boards/boards_util.js4
-rw-r--r--app/assets/javascripts/boards/components/board_card.vue2
-rw-r--r--app/assets/javascripts/boards/components/board_content_sidebar.vue36
-rw-r--r--app/assets/javascripts/boards/components/board_filtered_search.vue160
-rw-r--r--app/assets/javascripts/boards/components/board_form.vue82
-rw-r--r--app/assets/javascripts/boards/components/board_list_header.vue33
-rw-r--r--app/assets/javascripts/boards/components/boards_selector.vue88
-rw-r--r--app/assets/javascripts/boards/components/issue_board_filtered_search.vue53
-rw-r--r--app/assets/javascripts/boards/components/new_board_button.vue47
-rw-r--r--app/assets/javascripts/boards/components/sidebar/board_sidebar_labels_select.vue6
-rw-r--r--app/assets/javascripts/boards/graphql.js1
-rw-r--r--app/assets/javascripts/boards/graphql/board_list_create.mutation.graphql2
-rw-r--r--app/assets/javascripts/boards/graphql/board_lists.query.graphql2
-rw-r--r--app/assets/javascripts/boards/graphql/board_scope.fragment.graphql6
-rw-r--r--app/assets/javascripts/boards/graphql/group_board.query.graphql9
-rw-r--r--app/assets/javascripts/boards/graphql/group_board_iterations.query.graphql10
-rw-r--r--app/assets/javascripts/boards/graphql/group_projects.query.graphql2
-rw-r--r--app/assets/javascripts/boards/graphql/issue_move_list.mutation.graphql2
-rw-r--r--app/assets/javascripts/boards/graphql/lists_issues.query.graphql2
-rw-r--r--app/assets/javascripts/boards/graphql/project_board.query.graphql9
-rw-r--r--app/assets/javascripts/boards/graphql/project_board_iterations.query.graphql10
-rw-r--r--app/assets/javascripts/boards/graphql/project_milestones.query.graphql2
-rw-r--r--app/assets/javascripts/boards/index.js8
-rw-r--r--app/assets/javascripts/boards/mount_filtered_search_issue_boards.js5
-rw-r--r--app/assets/javascripts/boards/mount_multiple_boards_switcher.js19
-rw-r--r--app/assets/javascripts/boards/new_board.js29
-rw-r--r--app/assets/javascripts/boards/stores/actions.js95
-rw-r--r--app/assets/javascripts/boards/stores/mutation_types.js5
-rw-r--r--app/assets/javascripts/boards/stores/mutations.js18
-rw-r--r--app/assets/javascripts/boards/stores/state.js1
30 files changed, 448 insertions, 300 deletions
diff --git a/app/assets/javascripts/boards/boards_util.js b/app/assets/javascripts/boards/boards_util.js
index c10241d00d7..e6c91c7ac1f 100644
--- a/app/assets/javascripts/boards/boards_util.js
+++ b/app/assets/javascripts/boards/boards_util.js
@@ -1,4 +1,5 @@
import { sortBy, cloneDeep } from 'lodash';
+import { isGid } from '~/graphql_shared/utils';
import { ListType, MilestoneIDs } from './constants';
export function getMilestone() {
@@ -95,6 +96,9 @@ export function fullMilestoneId(id) {
}
export function fullLabelId(label) {
+ if (isGid(label.id)) {
+ return label.id;
+ }
if (label.project_id && label.project_id !== null) {
return `gid://gitlab/ProjectLabel/${label.id}`;
}
diff --git a/app/assets/javascripts/boards/components/board_card.vue b/app/assets/javascripts/boards/components/board_card.vue
index 1e780f9ef84..563bed6a6b8 100644
--- a/app/assets/javascripts/boards/components/board_card.vue
+++ b/app/assets/javascripts/boards/components/board_card.vue
@@ -83,7 +83,7 @@ export default {
:data-item-path="item.referencePath"
data-testid="board_card"
class="board-card gl-p-5 gl-rounded-base"
- @mouseup="toggleIssue($event)"
+ @click="toggleIssue($event)"
>
<board-card-inner :list="list" :item="item" :update-filters="true" />
</li>
diff --git a/app/assets/javascripts/boards/components/board_content_sidebar.vue b/app/assets/javascripts/boards/components/board_content_sidebar.vue
index 9bbb8a1a1b2..54668c9e88e 100644
--- a/app/assets/javascripts/boards/components/board_content_sidebar.vue
+++ b/app/assets/javascripts/boards/components/board_content_sidebar.vue
@@ -15,6 +15,7 @@ import SidebarDateWidget from '~/sidebar/components/date/sidebar_date_widget.vue
import SidebarSubscriptionsWidget from '~/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue';
import SidebarTodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue';
import SidebarLabelsWidget from '~/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue';
+import { LabelType } from '~/vue_shared/components/sidebar/labels_select_widget/constants';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
@@ -53,6 +54,9 @@ export default {
allowLabelEdit: {
default: false,
},
+ labelsFilterBasePath: {
+ default: '',
+ },
},
inheritAttrs: false,
computed: {
@@ -63,7 +67,7 @@ export default {
'groupPathForActiveIssue',
'projectPathForActiveIssue',
]),
- ...mapState(['sidebarType', 'issuableType', 'isSettingLabels']),
+ ...mapState(['sidebarType', 'issuableType']),
isIssuableSidebar() {
return this.sidebarType === ISSUABLE;
},
@@ -84,7 +88,15 @@ export default {
});
},
attrWorkspacePath() {
- return this.isGroupBoard ? this.groupPathForActiveIssue : undefined;
+ return this.isGroupBoard ? this.groupPathForActiveIssue : this.projectPathForActiveIssue;
+ },
+ labelType() {
+ return this.isGroupBoard ? LabelType.group : LabelType.project;
+ },
+ labelsFilterPath() {
+ return this.isGroupBoard
+ ? this.labelsFilterBasePath.replace(':project_path', this.projectPathForActiveIssue)
+ : this.labelsFilterBasePath;
},
},
methods: {
@@ -98,21 +110,19 @@ export default {
handleClose() {
this.toggleBoardItem({ boardItem: this.activeBoardItem, sidebarType: this.sidebarType });
},
- handleUpdateSelectedLabels(input) {
+ handleUpdateSelectedLabels({ labels, id }) {
this.setActiveBoardItemLabels({
- iid: this.activeBoardItem.iid,
+ id,
projectPath: this.projectPathForActiveIssue,
- addLabelIds: input.map((label) => getIdFromGraphQLId(label.id)),
- removeLabelIds: this.activeBoardItem.labels
- .filter((label) => !input.find((selected) => selected.id === label.id))
- .map((label) => label.id),
+ labelIds: labels.map((label) => getIdFromGraphQLId(label.id)),
+ labels,
});
},
- handleLabelRemove(input) {
+ handleLabelRemove(removeLabelId) {
this.setActiveBoardItemLabels({
iid: this.activeBoardItem.iid,
projectPath: this.projectPathForActiveIssue,
- removeLabelIds: [input],
+ removeLabelIds: [removeLabelId],
});
},
},
@@ -207,14 +217,14 @@ export default {
:full-path="projectPathForActiveIssue"
:allow-label-remove="allowLabelEdit"
:allow-multiselect="true"
- :selected-labels="activeBoardItem.labels"
- :labels-select-in-progress="isSettingLabels"
:footer-create-label-title="createLabelTitle"
:footer-manage-label-title="manageLabelTitle"
:labels-create-title="createLabelTitle"
- :labels-filter-base-path="projectPathForActiveIssue"
+ :labels-filter-base-path="labelsFilterPath"
:attr-workspace-path="attrWorkspacePath"
+ workspace-type="project"
:issuable-type="issuableType"
+ :label-create-type="labelType"
@onLabelRemove="handleLabelRemove"
@updateSelectedLabels="handleUpdateSelectedLabels"
>
diff --git a/app/assets/javascripts/boards/components/board_filtered_search.vue b/app/assets/javascripts/boards/components/board_filtered_search.vue
index 7f242dea644..6e6ada2d109 100644
--- a/app/assets/javascripts/boards/components/board_filtered_search.vue
+++ b/app/assets/javascripts/boards/components/board_filtered_search.vue
@@ -1,6 +1,7 @@
<script>
-import { pickBy } from 'lodash';
+import { pickBy, isEmpty } from 'lodash';
import { mapActions } from 'vuex';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { updateHistory, setUrlParams } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
@@ -19,6 +20,11 @@ export default {
type: Array,
required: true,
},
+ eeFilters: {
+ required: false,
+ type: Object,
+ default: () => ({}),
+ },
},
data() {
return {
@@ -26,57 +32,6 @@ export default {
};
},
computed: {
- urlParams() {
- const {
- authorUsername,
- labelName,
- assigneeUsername,
- search,
- milestoneTitle,
- types,
- weight,
- } = this.filterParams;
- let notParams = {};
-
- if (Object.prototype.hasOwnProperty.call(this.filterParams, 'not')) {
- notParams = pickBy(
- {
- 'not[label_name][]': this.filterParams.not.labelName,
- 'not[author_username]': this.filterParams.not.authorUsername,
- 'not[assignee_username]': this.filterParams.not.assigneeUsername,
- 'not[types]': this.filterParams.not.types,
- 'not[milestone_title]': this.filterParams.not.milestoneTitle,
- 'not[weight]': this.filterParams.not.weight,
- },
- undefined,
- );
- }
-
- return {
- ...notParams,
- author_username: authorUsername,
- 'label_name[]': labelName,
- assignee_username: assigneeUsername,
- milestone_title: milestoneTitle,
- search,
- types,
- weight,
- };
- },
- },
- methods: {
- ...mapActions(['performSearch']),
- handleFilter(filters) {
- this.filterParams = this.getFilterParams(filters);
-
- updateHistory({
- url: setUrlParams(this.urlParams, window.location.href, true, false, true),
- title: document.title,
- replace: true,
- });
-
- this.performSearch();
- },
getFilteredSearchValue() {
const {
authorUsername,
@@ -86,6 +41,8 @@ export default {
milestoneTitle,
types,
weight,
+ epicId,
+ myReactionEmoji,
} = this.filterParams;
const filteredSearchValue = [];
@@ -133,6 +90,20 @@ export default {
});
}
+ if (myReactionEmoji) {
+ filteredSearchValue.push({
+ type: 'my_reaction_emoji',
+ value: { data: myReactionEmoji, operator: '=' },
+ });
+ }
+
+ if (epicId) {
+ filteredSearchValue.push({
+ type: 'epic_id',
+ value: { data: epicId, operator: '=' },
+ });
+ }
+
if (this.filterParams['not[authorUsername]']) {
filteredSearchValue.push({
type: 'author_username',
@@ -177,12 +148,89 @@ export default {
});
}
+ if (this.filterParams['not[epicId]']) {
+ filteredSearchValue.push({
+ type: 'epic_id',
+ value: { data: this.filterParams['not[epicId]'], operator: '!=' },
+ });
+ }
+
+ if (this.filterParams['not[myReactionEmoji]']) {
+ filteredSearchValue.push({
+ type: 'my_reaction_emoji',
+ value: { data: this.filterParams['not[myReactionEmoji]'], operator: '!=' },
+ });
+ }
+
if (search) {
filteredSearchValue.push(search);
}
return filteredSearchValue;
},
+ urlParams() {
+ const {
+ authorUsername,
+ labelName,
+ assigneeUsername,
+ search,
+ milestoneTitle,
+ types,
+ weight,
+ epicId,
+ myReactionEmoji,
+ } = this.filterParams;
+
+ let notParams = {};
+
+ if (Object.prototype.hasOwnProperty.call(this.filterParams, 'not')) {
+ notParams = pickBy(
+ {
+ 'not[label_name][]': this.filterParams.not.labelName,
+ 'not[author_username]': this.filterParams.not.authorUsername,
+ 'not[assignee_username]': this.filterParams.not.assigneeUsername,
+ 'not[types]': this.filterParams.not.types,
+ 'not[milestone_title]': this.filterParams.not.milestoneTitle,
+ 'not[weight]': this.filterParams.not.weight,
+ 'not[epic_id]': this.filterParams.not.epicId,
+ 'not[my_reaction_emoji]': this.filterParams.not.myReactionEmoji,
+ },
+ undefined,
+ );
+ }
+
+ return {
+ ...notParams,
+ author_username: authorUsername,
+ 'label_name[]': labelName,
+ assignee_username: assigneeUsername,
+ milestone_title: milestoneTitle,
+ search,
+ types,
+ weight,
+ epic_id: getIdFromGraphQLId(epicId),
+ my_reaction_emoji: myReactionEmoji,
+ };
+ },
+ },
+ created() {
+ if (!isEmpty(this.eeFilters)) {
+ this.filterParams = this.eeFilters;
+ }
+ },
+ methods: {
+ ...mapActions(['performSearch']),
+ handleFilter(filters) {
+ this.filterParams = this.getFilterParams(filters);
+
+ updateHistory({
+ url: setUrlParams(this.urlParams, window.location.href, true, false, true),
+ title: document.title,
+ replace: true,
+ });
+
+ this.performSearch();
+ },
getFilterParams(filters = []) {
const notFilters = filters.filter((item) => item.value.operator === '!=');
const equalsFilters = filters.filter(
@@ -216,6 +264,12 @@ export default {
case 'weight':
filterParams.weight = filter.value.data;
break;
+ case 'epic_id':
+ filterParams.epicId = filter.value.data;
+ break;
+ case 'my_reaction_emoji':
+ filterParams.myReactionEmoji = filter.value.data;
+ break;
case 'filtered-search-term':
if (filter.value.data) plainText.push(filter.value.data);
break;
@@ -243,7 +297,7 @@ export default {
namespace=""
:tokens="tokens"
:search-input-placeholder="$options.i18n.search"
- :initial-filter-value="getFilteredSearchValue()"
+ :initial-filter-value="getFilteredSearchValue"
@onFilter="handleFilter"
/>
</template>
diff --git a/app/assets/javascripts/boards/components/board_form.vue b/app/assets/javascripts/boards/components/board_form.vue
index e939f0c0ebe..6ad57fd8985 100644
--- a/app/assets/javascripts/boards/components/board_form.vue
+++ b/app/assets/javascripts/boards/components/board_form.vue
@@ -2,10 +2,10 @@
import { GlModal, GlAlert } from '@gitlab/ui';
import { mapGetters, mapActions, mapState } from 'vuex';
import { TYPE_USER, TYPE_ITERATION, TYPE_MILESTONE } from '~/graphql_shared/constants';
-import { convertToGraphQLId } from '~/graphql_shared/utils';
+import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils';
import { getParameterByName, visitUrl } from '~/lib/utils/url_utility';
import { __, s__ } from '~/locale';
-import { fullLabelId, fullBoardId } from '../boards_util';
+import { fullLabelId } from '../boards_util';
import { formType } from '../constants';
import createBoardMutation from '../graphql/board_create.mutation.graphql';
@@ -18,11 +18,11 @@ const boardDefaults = {
name: '',
labels: [],
milestone: {},
- iteration_id: undefined,
+ iteration: {},
assignee: {},
weight: null,
- hide_backlog_list: false,
- hide_closed_list: false,
+ hideBacklogList: false,
+ hideClosedList: false,
};
export default {
@@ -57,39 +57,16 @@ export default {
type: Boolean,
required: true,
},
- labelsPath: {
- type: String,
- required: true,
- },
- labelsWebUrl: {
- type: String,
- required: true,
- },
scopedIssueBoardFeatureEnabled: {
type: Boolean,
required: false,
default: false,
},
- projectId: {
- type: Number,
- required: false,
- default: 0,
- },
- groupId: {
- type: Number,
- required: false,
- default: 0,
- },
weights: {
type: Array,
required: false,
default: () => [],
},
- enableScopedLabels: {
- type: Boolean,
- required: false,
- default: false,
- },
currentBoard: {
type: Object,
required: true,
@@ -167,17 +144,16 @@ export default {
return destroyBoardMutation;
},
baseMutationVariables() {
- const { board } = this;
- const variables = {
- name: board.name,
- hideBacklogList: board.hide_backlog_list,
- hideClosedList: board.hide_closed_list,
- };
+ const {
+ board: { name, hideBacklogList, hideClosedList, id },
+ } = this;
- return board.id
+ const variables = { name, hideBacklogList, hideClosedList };
+
+ return id
? {
...variables,
- id: fullBoardId(board.id),
+ id,
}
: {
...variables,
@@ -191,11 +167,13 @@ export default {
assigneeId: this.board.assignee?.id
? convertToGraphQLId(TYPE_USER, this.board.assignee.id)
: null,
+ // Temporarily converting to milestone ID due to https://gitlab.com/gitlab-org/gitlab/-/issues/344779
milestoneId: this.board.milestone?.id
- ? convertToGraphQLId(TYPE_MILESTONE, this.board.milestone.id)
+ ? convertToGraphQLId(TYPE_MILESTONE, getIdFromGraphQLId(this.board.milestone.id))
: null,
- iterationId: this.board.iteration_id
- ? convertToGraphQLId(TYPE_ITERATION, this.board.iteration_id)
+ // Temporarily converting to iteration ID due to https://gitlab.com/gitlab-org/gitlab/-/issues/344779
+ iterationId: this.board.iteration?.id
+ ? convertToGraphQLId(TYPE_ITERATION, getIdFromGraphQLId(this.board.iteration.id))
: null,
};
},
@@ -249,7 +227,7 @@ export default {
await this.$apollo.mutate({
mutation: this.deleteMutation,
variables: {
- id: fullBoardId(this.board.id),
+ id: this.board.id,
},
});
},
@@ -285,19 +263,12 @@ export default {
}
},
setIteration(iterationId) {
- this.board.iteration_id = iterationId;
+ this.$set(this.board, 'iteration', {
+ id: iterationId,
+ });
},
setBoardLabels(labels) {
- labels.forEach((label) => {
- if (label.set && !this.board.labels.find((l) => l.id === label.id)) {
- this.board.labels.push({
- ...label,
- textColor: label.text_color,
- });
- } else if (!label.set) {
- this.board.labels = this.board.labels.filter((selected) => selected.id !== label.id);
- }
- });
+ this.board.labels = labels;
},
setAssignee(assigneeId) {
this.$set(this.board, 'assignee', {
@@ -361,8 +332,8 @@ export default {
</div>
<board-configuration-options
- :hide-backlog-list.sync="board.hide_backlog_list"
- :hide-closed-list.sync="board.hide_closed_list"
+ :hide-backlog-list.sync="board.hideBacklogList"
+ :hide-closed-list.sync="board.hideClosedList"
:readonly="readonly"
/>
@@ -371,11 +342,6 @@ export default {
:collapse-scope="isNewForm"
:board="board"
:can-admin-board="canAdminBoard"
- :labels-path="labelsPath"
- :labels-web-url="labelsWebUrl"
- :enable-scoped-labels="enableScopedLabels"
- :project-id="projectId"
- :group-id="groupId"
:weights="weights"
@set-iteration="setIteration"
@set-board-labels="setBoardLabels"
diff --git a/app/assets/javascripts/boards/components/board_list_header.vue b/app/assets/javascripts/boards/components/board_list_header.vue
index a8d71ab7a35..e985a368e64 100644
--- a/app/assets/javascripts/boards/components/board_list_header.vue
+++ b/app/assets/javascripts/boards/components/board_list_header.vue
@@ -15,6 +15,8 @@ import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants';
import { n__, s__, __ } from '~/locale';
import sidebarEventHub from '~/sidebar/event_hub';
import Tracking from '~/tracking';
+import { formatDate } from '~/lib/utils/datetime_utility';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import AccessorUtilities from '../../lib/utils/accessor';
import { inactiveId, LIST, ListType, toggleFormEventPrefix } from '../constants';
import eventHub from '../eventhub';
@@ -40,7 +42,7 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
- mixins: [Tracking.mixin()],
+ mixins: [Tracking.mixin(), glFeatureFlagMixin()],
inject: {
boardId: {
default: '',
@@ -86,6 +88,13 @@ export default {
listTitle() {
return this.list?.label?.description || this.list?.assignee?.name || this.list.title || '';
},
+ listIterationPeriod() {
+ const iteration = this.list?.iteration;
+ return iteration ? this.getIterationPeriod(iteration) : '';
+ },
+ isIterationList() {
+ return this.listType === ListType.iteration;
+ },
showListHeaderButton() {
return !this.disabled && this.listType !== ListType.closed;
},
@@ -96,7 +105,10 @@ export default {
return this.listType === ListType.assignee && this.showListDetails;
},
showIterationListDetails() {
- return this.listType === ListType.iteration && this.showListDetails;
+ return this.isIterationList && this.showListDetails;
+ },
+ iterationCadencesAvailable() {
+ return this.isIterationList && this.glFeatures.iterationCadences;
},
showListDetails() {
return !this.list.collapsed || !this.isSwimlanesHeader;
@@ -208,6 +220,16 @@ export default {
updateListFunction() {
this.updateList({ listId: this.list.id, collapsed: this.list.collapsed });
},
+ /**
+ * TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/344619
+ * This method also exists as a utility function in ee/../iterations/utils.js
+ * Remove the duplication when the EE code is separated from this compoment.
+ */
+ getIterationPeriod({ startDate, dueDate }) {
+ const start = formatDate(startDate, 'mmm d, yyyy', true);
+ const due = formatDate(dueDate, 'mmm d, yyyy', true);
+ return `${start} - ${due}`;
+ },
},
};
</script>
@@ -307,6 +329,13 @@ export default {
class="board-title-main-text gl-text-truncate"
>
{{ listTitle }}
+ <span
+ v-if="iterationCadencesAvailable"
+ class="gl-display-inline-block gl-text-gray-400"
+ data-testid="board-list-iteration-period"
+ >
+ {{ listIterationPeriod }}</span
+ >
</span>
<span
v-if="listType === 'assignee'"
diff --git a/app/assets/javascripts/boards/components/boards_selector.vue b/app/assets/javascripts/boards/components/boards_selector.vue
index 98027917221..71facba1378 100644
--- a/app/assets/javascripts/boards/components/boards_selector.vue
+++ b/app/assets/javascripts/boards/components/boards_selector.vue
@@ -9,17 +9,20 @@ import {
GlModalDirective,
} from '@gitlab/ui';
import { throttle } from 'lodash';
-import { mapGetters, mapState } from 'vuex';
+import { mapActions, mapGetters, mapState } from 'vuex';
import BoardForm from 'ee_else_ce/boards/components/board_form.vue';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import axios from '~/lib/utils/axios_utils';
import httpStatusCodes from '~/lib/utils/http_status';
+import { s__ } from '~/locale';
import eventHub from '../eventhub';
-import groupQuery from '../graphql/group_boards.query.graphql';
-import projectQuery from '../graphql/project_boards.query.graphql';
+import groupBoardsQuery from '../graphql/group_boards.query.graphql';
+import projectBoardsQuery from '../graphql/project_boards.query.graphql';
+import groupBoardQuery from '../graphql/group_board.query.graphql';
+import projectBoardQuery from '../graphql/project_board.query.graphql';
const MIN_BOARDS_TO_VIEW_RECENT = 10;
@@ -39,10 +42,6 @@ export default {
},
inject: ['fullPath', 'recentBoardsEndpoint'],
props: {
- currentBoard: {
- type: Object,
- required: true,
- },
throttleDuration: {
type: Number,
default: 200,
@@ -64,22 +63,6 @@ export default {
type: Boolean,
required: true,
},
- labelsPath: {
- type: String,
- required: true,
- },
- labelsWebUrl: {
- type: String,
- required: true,
- },
- projectId: {
- type: Number,
- required: true,
- },
- groupId: {
- type: Number,
- required: true,
- },
scopedIssueBoardFeatureEnabled: {
type: Boolean,
required: true,
@@ -88,11 +71,6 @@ export default {
type: Array,
required: true,
},
- enabledScopedLabels: {
- type: Boolean,
- required: false,
- default: false,
- },
},
data() {
return {
@@ -107,14 +85,47 @@ export default {
maxPosition: 0,
filterTerm: '',
currentPage: '',
+ board: {},
};
},
+ apollo: {
+ board: {
+ query() {
+ return this.currentBoardQuery;
+ },
+ variables() {
+ return {
+ fullPath: this.fullPath,
+ boardId: this.fullBoardId,
+ };
+ },
+ update(data) {
+ const board = data.workspace?.board;
+ return {
+ ...board,
+ labels: board?.labels?.nodes,
+ };
+ },
+ error() {
+ this.setError({ message: this.$options.i18n.errorFetchingBoard });
+ },
+ },
+ },
computed: {
- ...mapState(['boardType']),
- ...mapGetters(['isGroupBoard']),
+ ...mapState(['boardType', 'fullBoardId']),
+ ...mapGetters(['isGroupBoard', 'isProjectBoard']),
parentType() {
return this.boardType;
},
+ currentBoardQueryCE() {
+ return this.isGroupBoard ? groupBoardQuery : projectBoardQuery;
+ },
+ currentBoardQuery() {
+ return this.currentBoardQueryCE;
+ },
+ isBoardLoading() {
+ return this.$apollo.queries.board.loading;
+ },
loading() {
return this.loadingRecentBoards || Boolean(this.loadingBoards);
},
@@ -123,9 +134,6 @@ export default {
board.name.toLowerCase().includes(this.filterTerm.toLowerCase()),
);
},
- board() {
- return this.currentBoard;
- },
showCreate() {
return this.multipleIssueBoardsAvailable;
},
@@ -158,6 +166,7 @@ export default {
eventHub.$off('showBoardModal', this.showPage);
},
methods: {
+ ...mapActions(['setError']),
showPage(page) {
this.currentPage = page;
},
@@ -174,7 +183,7 @@ export default {
}));
},
boardQuery() {
- return this.isGroupBoard ? groupQuery : projectQuery;
+ return this.isGroupBoard ? groupBoardsQuery : projectBoardsQuery;
},
loadBoards(toggleDropdown = true) {
if (toggleDropdown && this.boards.length > 0) {
@@ -250,6 +259,9 @@ export default {
this.hasScrollFade = this.isScrolledUp();
},
},
+ i18n: {
+ errorFetchingBoard: s__('Board|An error occurred while fetching the board, please try again.'),
+ },
};
</script>
@@ -260,6 +272,7 @@ export default {
data-qa-selector="boards_dropdown"
toggle-class="dropdown-menu-toggle js-dropdown-toggle"
menu-class="flex-column dropdown-extended-height"
+ :loading="isBoardLoading"
:text="board.name"
@show="loadBoards"
>
@@ -354,15 +367,10 @@ export default {
<board-form
v-if="currentPage"
- :labels-path="labelsPath"
- :labels-web-url="labelsWebUrl"
- :project-id="projectId"
- :group-id="groupId"
:can-admin-board="canAdminBoard"
:scoped-issue-board-feature-enabled="scopedIssueBoardFeatureEnabled"
:weights="weights"
- :enable-scoped-labels="enabledScopedLabels"
- :current-board="currentBoard"
+ :current-board="board"
:current-page="currentPage"
@cancel="cancel"
/>
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 b6c5ef955c6..bdb9c2be836 100644
--- a/app/assets/javascripts/boards/components/issue_board_filtered_search.vue
+++ b/app/assets/javascripts/boards/components/issue_board_filtered_search.vue
@@ -1,13 +1,20 @@
<script>
import { GlFilteredSearchToken } from '@gitlab/ui';
+import fuzzaldrinPlus from 'fuzzaldrin-plus';
import { mapActions } from 'vuex';
-import BoardFilteredSearch from '~/boards/components/board_filtered_search.vue';
+import BoardFilteredSearch from 'ee_else_ce/boards/components/board_filtered_search.vue';
+import { BoardType } from '~/boards/constants';
+import axios from '~/lib/utils/axios_utils';
import issueBoardFilters from '~/boards/issue_board_filters';
import { TYPE_USER } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import { __ } from '~/locale';
-import { DEFAULT_MILESTONES_GRAPHQL } from '~/vue_shared/components/filtered_search_bar/constants';
+import {
+ DEFAULT_MILESTONES_GRAPHQL,
+ TOKEN_TITLE_MY_REACTION,
+} from '~/vue_shared/components/filtered_search_bar/constants';
import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
+import EmojiToken from '~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue';
import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue';
import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue';
import WeightToken from '~/vue_shared/components/filtered_search_bar/tokens/weight_token.vue';
@@ -19,6 +26,7 @@ export default {
},
i18n: {
search: __('Search'),
+ epic: __('Epic'),
label: __('Label'),
author: __('Author'),
assignee: __('Assignee'),
@@ -31,6 +39,7 @@ export default {
isNot: __('is not'),
},
components: { BoardFilteredSearch },
+ inject: ['isSignedIn'],
props: {
fullPath: {
type: String,
@@ -42,7 +51,15 @@ export default {
},
},
computed: {
- tokens() {
+ isGroupBoard() {
+ return this.boardType === BoardType.group;
+ },
+ epicsGroupPath() {
+ return this.isGroupBoard
+ ? this.fullPath
+ : this.fullPath.slice(0, this.fullPath.lastIndexOf('/'));
+ },
+ tokensCE() {
const {
label,
is,
@@ -103,6 +120,32 @@ export default {
symbol: '~',
fetchLabels,
},
+ ...(this.isSignedIn
+ ? [
+ {
+ type: 'my_reaction_emoji',
+ title: TOKEN_TITLE_MY_REACTION,
+ icon: 'thumb-up',
+ token: EmojiToken,
+ unique: true,
+ fetchEmojis: (search = '') => {
+ // TODO: Switch to GraphQL query when backend is ready: https://gitlab.com/gitlab-org/gitlab/-/issues/339694
+ return axios
+ .get(`${gon.relative_url_root || ''}/-/autocomplete/award_emojis`)
+ .then(({ data }) => {
+ if (search) {
+ return {
+ data: fuzzaldrinPlus.filter(data, search, {
+ key: ['name'],
+ }),
+ };
+ }
+ return { data };
+ });
+ },
+ },
+ ]
+ : []),
{
type: 'milestone_title',
title: milestone,
@@ -117,7 +160,6 @@ export default {
icon: 'issues',
title: type,
type: 'types',
- operators: [{ value: '=', description: is }],
token: GlFilteredSearchToken,
unique: true,
options: [
@@ -134,6 +176,9 @@ export default {
},
];
},
+ tokens() {
+ return this.tokensCE;
+ },
},
methods: {
...mapActions(['fetchMilestones']),
diff --git a/app/assets/javascripts/boards/components/new_board_button.vue b/app/assets/javascripts/boards/components/new_board_button.vue
new file mode 100644
index 00000000000..f7914c636cc
--- /dev/null
+++ b/app/assets/javascripts/boards/components/new_board_button.vue
@@ -0,0 +1,47 @@
+<script>
+import { GlButton, GlModalDirective } from '@gitlab/ui';
+import { formType } from '~/boards/constants';
+import eventHub from '~/boards/eventhub';
+import { s__ } from '~/locale';
+import Tracking from '~/tracking';
+import GitlabExperiment from '~/experimentation/components/gitlab_experiment.vue';
+
+export default {
+ components: {
+ GlButton,
+ GitlabExperiment,
+ },
+ directives: {
+ GlModalDirective,
+ },
+ mixins: [Tracking.mixin()],
+ inject: ['multipleIssueBoardsAvailable', 'canAdminBoard'],
+ computed: {
+ canShowCreateButton() {
+ return this.canAdminBoard && this.multipleIssueBoardsAvailable;
+ },
+ createButtonText() {
+ return s__('Boards|New board');
+ },
+ },
+ methods: {
+ showDialog() {
+ this.track('click_button', { label: 'create_board' });
+ eventHub.$emit('showBoardModal', formType.new);
+ },
+ },
+};
+</script>
+
+<template>
+ <gitlab-experiment name="prominent_create_board_btn">
+ <template #control> </template>
+ <template #candidate>
+ <div v-if="canShowCreateButton" class="gl-ml-1 gl-mr-3 gl-display-flex gl-align-items-center">
+ <gl-button data-qa-selector="new_board_button" @click.prevent="showDialog">
+ {{ createButtonText }}
+ </gl-button>
+ </div>
+ </template>
+ </gitlab-experiment>
+</template>
diff --git a/app/assets/javascripts/boards/components/sidebar/board_sidebar_labels_select.vue b/app/assets/javascripts/boards/components/sidebar/board_sidebar_labels_select.vue
index e74463825c5..ec53947fd5f 100644
--- a/app/assets/javascripts/boards/components/sidebar/board_sidebar_labels_select.vue
+++ b/app/assets/javascripts/boards/components/sidebar/board_sidebar_labels_select.vue
@@ -91,9 +91,7 @@ export default {
try {
const addLabelIds = payload.filter((label) => label.set).map((label) => label.id);
- const removeLabelIds = this.selectedLabels
- .filter((label) => !payload.find((selected) => selected.id === label.id))
- .map((label) => label.id);
+ const removeLabelIds = payload.filter((label) => !label.set).map((label) => label.id);
const input = {
addLabelIds,
@@ -164,7 +162,7 @@ export default {
:labels-list-title="__('Select label')"
:dropdown-button-text="__('Choose labels')"
:is-editing="edit"
- variant="embedded"
+ variant="sidebar"
class="gl-display-block labels gl-w-full"
@updateSelectedLabels="setLabels"
>
diff --git a/app/assets/javascripts/boards/graphql.js b/app/assets/javascripts/boards/graphql.js
index d8d16184936..64938cb42ed 100644
--- a/app/assets/javascripts/boards/graphql.js
+++ b/app/assets/javascripts/boards/graphql.js
@@ -17,6 +17,5 @@ export const gqlClient = createDefaultClient(
fragmentMatcher,
},
- assumeImmutableResults: true,
},
);
diff --git a/app/assets/javascripts/boards/graphql/board_list_create.mutation.graphql b/app/assets/javascripts/boards/graphql/board_list_create.mutation.graphql
index 3eb23f62940..0e1d11727cf 100644
--- a/app/assets/javascripts/boards/graphql/board_list_create.mutation.graphql
+++ b/app/assets/javascripts/boards/graphql/board_list_create.mutation.graphql
@@ -1,6 +1,6 @@
#import "./board_list.fragment.graphql"
-mutation CreateBoardList($boardId: BoardID!, $backlog: Boolean, $labelId: LabelID) {
+mutation createBoardList($boardId: BoardID!, $backlog: Boolean, $labelId: LabelID) {
boardListCreate(input: { boardId: $boardId, backlog: $backlog, labelId: $labelId }) {
list {
...BoardListFragment
diff --git a/app/assets/javascripts/boards/graphql/board_lists.query.graphql b/app/assets/javascripts/boards/graphql/board_lists.query.graphql
index 734867c77e9..47e87907d76 100644
--- a/app/assets/javascripts/boards/graphql/board_lists.query.graphql
+++ b/app/assets/javascripts/boards/graphql/board_lists.query.graphql
@@ -1,6 +1,6 @@
#import "ee_else_ce/boards/graphql/board_list.fragment.graphql"
-query ListIssues(
+query BoardLists(
$fullPath: ID!
$boardId: ID!
$filters: BoardIssueInput
diff --git a/app/assets/javascripts/boards/graphql/board_scope.fragment.graphql b/app/assets/javascripts/boards/graphql/board_scope.fragment.graphql
new file mode 100644
index 00000000000..57f51822d91
--- /dev/null
+++ b/app/assets/javascripts/boards/graphql/board_scope.fragment.graphql
@@ -0,0 +1,6 @@
+fragment BoardScopeFragment on Board {
+ id
+ name
+ hideBacklogList
+ hideClosedList
+}
diff --git a/app/assets/javascripts/boards/graphql/group_board.query.graphql b/app/assets/javascripts/boards/graphql/group_board.query.graphql
new file mode 100644
index 00000000000..77c8e0378f0
--- /dev/null
+++ b/app/assets/javascripts/boards/graphql/group_board.query.graphql
@@ -0,0 +1,9 @@
+#import "ee_else_ce/boards/graphql/board_scope.fragment.graphql"
+
+query GroupBoard($fullPath: ID!, $boardId: ID!) {
+ workspace: group(fullPath: $fullPath) {
+ board(id: $boardId) {
+ ...BoardScopeFragment
+ }
+ }
+}
diff --git a/app/assets/javascripts/boards/graphql/group_board_iterations.query.graphql b/app/assets/javascripts/boards/graphql/group_board_iterations.query.graphql
deleted file mode 100644
index 1c382c4747b..00000000000
--- a/app/assets/javascripts/boards/graphql/group_board_iterations.query.graphql
+++ /dev/null
@@ -1,10 +0,0 @@
-query GroupBoardIterations($fullPath: ID!, $title: String) {
- group(fullPath: $fullPath) {
- iterations(includeAncestors: true, title: $title) {
- nodes {
- id
- title
- }
- }
- }
-}
diff --git a/app/assets/javascripts/boards/graphql/group_projects.query.graphql b/app/assets/javascripts/boards/graphql/group_projects.query.graphql
index 3218c06357c..c5732bbaff3 100644
--- a/app/assets/javascripts/boards/graphql/group_projects.query.graphql
+++ b/app/assets/javascripts/boards/graphql/group_projects.query.graphql
@@ -1,6 +1,6 @@
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
-query getGroupProjects($fullPath: ID!, $search: String, $after: String) {
+query boardsGetGroupProjects($fullPath: ID!, $search: String, $after: String) {
group(fullPath: $fullPath) {
projects(search: $search, after: $after, first: 100, includeSubgroups: true) {
nodes {
diff --git a/app/assets/javascripts/boards/graphql/issue_move_list.mutation.graphql b/app/assets/javascripts/boards/graphql/issue_move_list.mutation.graphql
index 3c574fd8c87..570731ecac6 100644
--- a/app/assets/javascripts/boards/graphql/issue_move_list.mutation.graphql
+++ b/app/assets/javascripts/boards/graphql/issue_move_list.mutation.graphql
@@ -1,6 +1,6 @@
#import "ee_else_ce/boards/graphql/issue.fragment.graphql"
-mutation IssueMoveList(
+mutation issueMoveList(
$projectPath: ID!
$iid: String!
$boardId: ID!
diff --git a/app/assets/javascripts/boards/graphql/lists_issues.query.graphql b/app/assets/javascripts/boards/graphql/lists_issues.query.graphql
index 787dd77b901..9f93bc6d5bf 100644
--- a/app/assets/javascripts/boards/graphql/lists_issues.query.graphql
+++ b/app/assets/javascripts/boards/graphql/lists_issues.query.graphql
@@ -1,6 +1,6 @@
#import "ee_else_ce/boards/graphql/issue.fragment.graphql"
-query ListIssues(
+query BoardListEE(
$fullPath: ID!
$boardId: ID!
$id: ID
diff --git a/app/assets/javascripts/boards/graphql/project_board.query.graphql b/app/assets/javascripts/boards/graphql/project_board.query.graphql
new file mode 100644
index 00000000000..6e4cd6bed57
--- /dev/null
+++ b/app/assets/javascripts/boards/graphql/project_board.query.graphql
@@ -0,0 +1,9 @@
+#import "ee_else_ce/boards/graphql/board_scope.fragment.graphql"
+
+query ProjectBoard($fullPath: ID!, $boardId: ID!) {
+ workspace: project(fullPath: $fullPath) {
+ board(id: $boardId) {
+ ...BoardScopeFragment
+ }
+ }
+}
diff --git a/app/assets/javascripts/boards/graphql/project_board_iterations.query.graphql b/app/assets/javascripts/boards/graphql/project_board_iterations.query.graphql
deleted file mode 100644
index 078151a275a..00000000000
--- a/app/assets/javascripts/boards/graphql/project_board_iterations.query.graphql
+++ /dev/null
@@ -1,10 +0,0 @@
-query ProjectBoardIterations($fullPath: ID!, $title: String) {
- project(fullPath: $fullPath) {
- iterations(includeAncestors: true, title: $title) {
- nodes {
- id
- title
- }
- }
- }
-}
diff --git a/app/assets/javascripts/boards/graphql/project_milestones.query.graphql b/app/assets/javascripts/boards/graphql/project_milestones.query.graphql
index 724b7f5a34c..61c9ddded9b 100644
--- a/app/assets/javascripts/boards/graphql/project_milestones.query.graphql
+++ b/app/assets/javascripts/boards/graphql/project_milestones.query.graphql
@@ -1,4 +1,4 @@
-query projectMilestones(
+query boardProjectMilestones(
$fullPath: ID!
$state: MilestoneStateEnum
$includeAncestors: Boolean
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index b6b1094fb3a..6fa8dd63245 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -13,9 +13,10 @@ import FilteredSearchBoards from '~/boards/filtered_search_boards';
import initBoardsFilteredSearch from '~/boards/mount_filtered_search_issue_boards';
import store from '~/boards/stores';
import toggleFocusMode from '~/boards/toggle_focus';
-import { NavigationType, parseBoolean } from '~/lib/utils/common_utils';
+import { NavigationType, isLoggedIn, parseBoolean } from '~/lib/utils/common_utils';
import { fullBoardId } from './boards_util';
import boardConfigToggle from './config_toggle';
+import initNewBoard from './new_board';
import { gqlClient } from './graphql';
import mountMultipleBoardsSwitcher from './mount_multiple_boards_switcher';
@@ -109,7 +110,7 @@ export default () => {
});
if (gon?.features?.issueBoardsFilteredSearch) {
- initBoardsFilteredSearch(apolloProvider);
+ initBoardsFilteredSearch(apolloProvider, isLoggedIn());
}
mountBoardApp($boardApp);
@@ -130,6 +131,7 @@ export default () => {
}
boardConfigToggle();
+ initNewBoard();
toggleFocusMode();
toggleLabels();
@@ -142,5 +144,7 @@ export default () => {
fullPath: $boardApp.dataset.fullPath,
rootPath: $boardApp.dataset.boardsEndpoint,
recentBoardsEndpoint: $boardApp.dataset.recentBoardsEndpoint,
+ allowScopedLabels: $boardApp.dataset.scopedLabels,
+ labelsManagePath: $boardApp.dataset.labelsManagePath,
});
};
diff --git a/app/assets/javascripts/boards/mount_filtered_search_issue_boards.js b/app/assets/javascripts/boards/mount_filtered_search_issue_boards.js
index 7732091ef34..1ea74d5685c 100644
--- a/app/assets/javascripts/boards/mount_filtered_search_issue_boards.js
+++ b/app/assets/javascripts/boards/mount_filtered_search_issue_boards.js
@@ -1,10 +1,10 @@
import Vue from 'vue';
-import IssueBoardFilteredSearch from '~/boards/components/issue_board_filtered_search.vue';
+import IssueBoardFilteredSearch from 'ee_else_ce/boards/components/issue_board_filtered_search.vue';
import store from '~/boards/stores';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { queryToObject } from '~/lib/utils/url_utility';
-export default (apolloProvider) => {
+export default (apolloProvider, isSignedIn) => {
const el = document.getElementById('js-issue-board-filtered-search');
const rawFilterParams = queryToObject(window.location.search, { gatherArrays: true });
@@ -20,6 +20,7 @@ export default (apolloProvider) => {
el,
provide: {
initialFilterParams,
+ isSignedIn,
},
store, // TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/324094
apolloProvider,
diff --git a/app/assets/javascripts/boards/mount_multiple_boards_switcher.js b/app/assets/javascripts/boards/mount_multiple_boards_switcher.js
index a3a8ad06c43..ed32579a9c3 100644
--- a/app/assets/javascripts/boards/mount_multiple_boards_switcher.js
+++ b/app/assets/javascripts/boards/mount_multiple_boards_switcher.js
@@ -1,23 +1,32 @@
+import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import BoardsSelector from 'ee_else_ce/boards/components/boards_selector.vue';
import store from '~/boards/stores';
import createDefaultClient from '~/lib/graphql';
import { parseBoolean } from '~/lib/utils/common_utils';
+import introspectionQueryResultData from '~/sidebar/fragmentTypes.json';
Vue.use(VueApollo);
+const fragmentMatcher = new IntrospectionFragmentMatcher({
+ introspectionQueryResultData,
+});
+
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(
{},
{
- assumeImmutableResults: true,
+ cacheConfig: {
+ fragmentMatcher,
+ },
},
),
});
export default (params = {}) => {
const boardsSwitcherElement = document.getElementById('js-multiple-boards-switcher');
+ const { dataset } = boardsSwitcherElement;
return new Vue({
el: boardsSwitcherElement,
components: {
@@ -29,18 +38,16 @@ export default (params = {}) => {
fullPath: params.fullPath,
rootPath: params.rootPath,
recentBoardsEndpoint: params.recentBoardsEndpoint,
+ allowScopedLabels: params.allowScopedLabels,
+ labelsManagePath: params.labelsManagePath,
+ allowLabelCreate: parseBoolean(dataset.canAdminBoard),
},
data() {
- const { dataset } = boardsSwitcherElement;
-
const boardsSelectorProps = {
...dataset,
- currentBoard: JSON.parse(dataset.currentBoard),
hasMissingBoards: parseBoolean(dataset.hasMissingBoards),
canAdminBoard: parseBoolean(dataset.canAdminBoard),
multipleIssueBoardsAvailable: parseBoolean(dataset.multipleIssueBoardsAvailable),
- projectId: dataset.projectId ? Number(dataset.projectId) : 0,
- groupId: Number(dataset.groupId),
scopedIssueBoardFeatureEnabled: parseBoolean(dataset.scopedIssueBoardFeatureEnabled),
weights: JSON.parse(dataset.weights),
};
diff --git a/app/assets/javascripts/boards/new_board.js b/app/assets/javascripts/boards/new_board.js
new file mode 100644
index 00000000000..34f2fea79a9
--- /dev/null
+++ b/app/assets/javascripts/boards/new_board.js
@@ -0,0 +1,29 @@
+import Vue from 'vue';
+import { parseBoolean } from '~/lib/utils/common_utils';
+import { getExperimentVariant } from '~/experimentation/utils';
+import { CANDIDATE_VARIANT } from '~/experimentation/constants';
+import NewBoardButton from './components/new_board_button.vue';
+
+export default () => {
+ if (getExperimentVariant('prominent_create_board_btn') !== CANDIDATE_VARIANT) {
+ return;
+ }
+
+ const el = document.querySelector('.js-new-board');
+
+ if (!el) {
+ return;
+ }
+
+ // eslint-disable-next-line no-new
+ new Vue({
+ el,
+ provide: {
+ multipleIssueBoardsAvailable: parseBoolean(el.dataset.multipleIssueBoardsAvailable),
+ canAdminBoard: parseBoolean(el.dataset.canAdminBoard),
+ },
+ render(h) {
+ return h(NewBoardButton);
+ },
+ });
+};
diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js
index ca993e75cf9..3a96e535cf7 100644
--- a/app/assets/javascripts/boards/stores/actions.js
+++ b/app/assets/javascripts/boards/stores/actions.js
@@ -36,13 +36,11 @@ import {
} from '../boards_util';
import { gqlClient } from '../graphql';
import boardLabelsQuery from '../graphql/board_labels.query.graphql';
-import groupBoardIterationsQuery from '../graphql/group_board_iterations.query.graphql';
import groupBoardMilestonesQuery from '../graphql/group_board_milestones.query.graphql';
import groupProjectsQuery from '../graphql/group_projects.query.graphql';
import issueCreateMutation from '../graphql/issue_create.mutation.graphql';
import issueSetLabelsMutation from '../graphql/issue_set_labels.mutation.graphql';
import listsIssuesQuery from '../graphql/lists_issues.query.graphql';
-import projectBoardIterationsQuery from '../graphql/project_board_iterations.query.graphql';
import projectBoardMilestonesQuery from '../graphql/project_board_milestones.query.graphql';
import * as types from './mutation_types';
@@ -203,52 +201,6 @@ export default {
});
},
- fetchIterations({ state, commit }, title) {
- commit(types.RECEIVE_ITERATIONS_REQUEST);
-
- const { fullPath, boardType } = state;
-
- const variables = {
- fullPath,
- title,
- };
-
- let query;
- if (boardType === BoardType.project) {
- query = projectBoardIterationsQuery;
- }
- if (boardType === BoardType.group) {
- query = groupBoardIterationsQuery;
- }
-
- if (!query) {
- // eslint-disable-next-line @gitlab/require-i18n-strings
- throw new Error('Unknown board type');
- }
-
- return gqlClient
- .query({
- query,
- variables,
- })
- .then(({ data }) => {
- const errors = data[boardType]?.errors;
- const iterations = data[boardType]?.iterations.nodes;
-
- if (errors?.[0]) {
- throw new Error(errors[0]);
- }
-
- commit(types.RECEIVE_ITERATIONS_SUCCESS, iterations);
-
- return iterations;
- })
- .catch((e) => {
- commit(types.RECEIVE_ITERATIONS_FAILURE);
- throw e;
- });
- },
-
fetchMilestones({ state, commit }, searchTerm) {
commit(types.RECEIVE_MILESTONES_REQUEST);
@@ -656,30 +608,45 @@ export default {
},
setActiveIssueLabels: async ({ commit, getters }, input) => {
- commit(types.SET_LABELS_LOADING, true);
const { activeBoardItem } = getters;
- const { data } = await gqlClient.mutate({
- mutation: issueSetLabelsMutation,
- variables: {
- input: {
- iid: input.iid || String(activeBoardItem.iid),
- addLabelIds: input.addLabelIds ?? [],
- removeLabelIds: input.removeLabelIds ?? [],
- projectPath: input.projectPath,
+
+ if (!gon.features?.labelsWidget) {
+ const { data } = await gqlClient.mutate({
+ mutation: issueSetLabelsMutation,
+ variables: {
+ input: {
+ iid: input.iid || String(activeBoardItem.iid),
+ labelIds: input.labelsId ?? undefined,
+ addLabelIds: input.addLabelIds ?? [],
+ removeLabelIds: input.removeLabelIds ?? [],
+ projectPath: input.projectPath,
+ },
},
- },
- });
+ });
- commit(types.SET_LABELS_LOADING, false);
+ if (data.updateIssue?.errors?.length > 0) {
+ throw new Error(data.updateIssue.errors);
+ }
+
+ commit(types.UPDATE_BOARD_ITEM_BY_ID, {
+ itemId: data.updateIssue?.issue?.id || activeBoardItem.id,
+ prop: 'labels',
+ value: data.updateIssue?.issue?.labels.nodes,
+ });
- if (data.updateIssue?.errors?.length > 0) {
- throw new Error(data.updateIssue.errors);
+ return;
}
+ let labels = input?.labels || [];
+ if (input.removeLabelIds) {
+ labels = activeBoardItem.labels.filter(
+ (label) => input.removeLabelIds[0] !== getIdFromGraphQLId(label.id),
+ );
+ }
commit(types.UPDATE_BOARD_ITEM_BY_ID, {
- itemId: data.updateIssue?.issue?.id || activeBoardItem.id,
+ itemId: input.id || activeBoardItem.id,
prop: 'labels',
- value: data.updateIssue.issue.labels.nodes,
+ value: labels,
});
},
diff --git a/app/assets/javascripts/boards/stores/mutation_types.js b/app/assets/javascripts/boards/stores/mutation_types.js
index 26b785932bb..31b78014525 100644
--- a/app/assets/javascripts/boards/stores/mutation_types.js
+++ b/app/assets/javascripts/boards/stores/mutation_types.js
@@ -28,7 +28,6 @@ export const ADD_BOARD_ITEM_TO_LIST = 'ADD_BOARD_ITEM_TO_LIST';
export const REMOVE_BOARD_ITEM_FROM_LIST = 'REMOVE_BOARD_ITEM_FROM_LIST';
export const SET_ACTIVE_ID = 'SET_ACTIVE_ID';
export const UPDATE_BOARD_ITEM_BY_ID = 'UPDATE_BOARD_ITEM_BY_ID';
-export const SET_LABELS_LOADING = 'SET_LABELS_LOADING';
export const SET_ASSIGNEE_LOADING = 'SET_ASSIGNEE_LOADING';
export const RESET_ISSUES = 'RESET_ISSUES';
export const REQUEST_GROUP_PROJECTS = 'REQUEST_GROUP_PROJECTS';
@@ -42,7 +41,3 @@ export const ADD_LIST_TO_HIGHLIGHTED_LISTS = 'ADD_LIST_TO_HIGHLIGHTED_LISTS';
export const REMOVE_LIST_FROM_HIGHLIGHTED_LISTS = 'REMOVE_LIST_FROM_HIGHLIGHTED_LISTS';
export const RESET_BOARD_ITEM_SELECTION = 'RESET_BOARD_ITEM_SELECTION';
export const SET_ERROR = 'SET_ERROR';
-
-export const RECEIVE_ITERATIONS_REQUEST = 'RECEIVE_ITERATIONS_REQUEST';
-export const RECEIVE_ITERATIONS_SUCCESS = 'RECEIVE_ITERATIONS_SUCCESS';
-export const RECEIVE_ITERATIONS_FAILURE = 'RECEIVE_ITERATIONS_FAILURE';
diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js
index d381c076c19..2a2ce7652e6 100644
--- a/app/assets/javascripts/boards/stores/mutations.js
+++ b/app/assets/javascripts/boards/stores/mutations.js
@@ -64,20 +64,6 @@ export default {
);
},
- [mutationTypes.RECEIVE_ITERATIONS_REQUEST](state) {
- state.iterationsLoading = true;
- },
-
- [mutationTypes.RECEIVE_ITERATIONS_SUCCESS](state, iterations) {
- state.iterations = iterations;
- state.iterationsLoading = false;
- },
-
- [mutationTypes.RECEIVE_ITERATIONS_FAILURE](state) {
- state.iterationsLoading = false;
- state.error = __('Failed to load iterations.');
- },
-
[mutationTypes.SET_ACTIVE_ID](state, { id, sidebarType }) {
state.activeId = id;
state.sidebarType = sidebarType;
@@ -195,10 +181,6 @@ export default {
Vue.set(state.boardItems[itemId], prop, value);
},
- [mutationTypes.SET_LABELS_LOADING](state, isLoading) {
- state.isSettingLabels = isLoading;
- },
-
[mutationTypes.SET_ASSIGNEE_LOADING](state, isLoading) {
state.isSettingAssignees = isLoading;
},
diff --git a/app/assets/javascripts/boards/stores/state.js b/app/assets/javascripts/boards/stores/state.js
index 2a6605e687b..80c51c966d2 100644
--- a/app/assets/javascripts/boards/stores/state.js
+++ b/app/assets/javascripts/boards/stores/state.js
@@ -12,7 +12,6 @@ export default () => ({
listsFlags: {},
boardItemsByListId: {},
backupItemsList: [],
- isSettingLabels: false,
isSettingAssignees: false,
pageInfoByListId: {},
boardItems: {},