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_add_new_column_trigger.vue10
-rw-r--r--app/assets/javascripts/boards/components/board_card.vue16
-rw-r--r--app/assets/javascripts/boards/components/board_card_inner.vue14
-rw-r--r--app/assets/javascripts/boards/components/board_column.vue15
-rw-r--r--app/assets/javascripts/boards/components/board_column_deprecated.vue7
-rw-r--r--app/assets/javascripts/boards/components/board_content.vue1
-rw-r--r--app/assets/javascripts/boards/components/board_content_sidebar.vue67
-rw-r--r--app/assets/javascripts/boards/components/board_filtered_search.vue5
-rw-r--r--app/assets/javascripts/boards/components/board_form.vue51
-rw-r--r--app/assets/javascripts/boards/components/board_list.vue89
-rw-r--r--app/assets/javascripts/boards/components/board_list_deprecated.vue13
-rw-r--r--app/assets/javascripts/boards/components/board_list_header.vue20
-rw-r--r--app/assets/javascripts/boards/components/board_list_header_deprecated.vue16
-rw-r--r--app/assets/javascripts/boards/components/board_new_issue.vue2
-rw-r--r--app/assets/javascripts/boards/components/board_settings_sidebar.vue4
-rw-r--r--app/assets/javascripts/boards/components/config_toggle.vue3
-rw-r--r--app/assets/javascripts/boards/components/issue_card_inner_deprecated.vue6
-rw-r--r--app/assets/javascripts/boards/components/issue_time_estimate_deprecated.vue2
-rw-r--r--app/assets/javascripts/boards/components/sidebar/board_sidebar_due_date.vue110
-rw-r--r--app/assets/javascripts/boards/components/sidebar/board_sidebar_labels_select.vue11
-rw-r--r--app/assets/javascripts/boards/components/sidebar/board_sidebar_milestone_select.vue158
-rw-r--r--app/assets/javascripts/boards/components/sidebar/board_sidebar_subscription.vue6
-rw-r--r--app/assets/javascripts/boards/components/sidebar/board_sidebar_time_tracker.vue20
-rw-r--r--app/assets/javascripts/boards/components/sidebar/board_sidebar_title.vue5
24 files changed, 255 insertions, 396 deletions
diff --git a/app/assets/javascripts/boards/components/board_add_new_column_trigger.vue b/app/assets/javascripts/boards/components/board_add_new_column_trigger.vue
index 85f001d9d61..2aee84b805f 100644
--- a/app/assets/javascripts/boards/components/board_add_new_column_trigger.vue
+++ b/app/assets/javascripts/boards/components/board_add_new_column_trigger.vue
@@ -1,21 +1,25 @@
<script>
import { GlButton } from '@gitlab/ui';
import { mapActions } from 'vuex';
+import Tracking from '~/tracking';
export default {
components: {
GlButton,
},
+ mixins: [Tracking.mixin()],
methods: {
...mapActions(['setAddColumnFormVisibility']),
+ handleClick() {
+ this.setAddColumnFormVisibility(true);
+ this.track('click_button', { label: 'create_list' });
+ },
},
};
</script>
<template>
<div class="gl-ml-3 gl-display-flex gl-align-items-center" data-testid="boards-create-list">
- <gl-button variant="confirm" @click="setAddColumnFormVisibility(true)"
- >{{ __('Create list') }}
- </gl-button>
+ <gl-button variant="confirm" @click="handleClick">{{ __('Create list') }} </gl-button>
</div>
</template>
diff --git a/app/assets/javascripts/boards/components/board_card.vue b/app/assets/javascripts/boards/components/board_card.vue
index 2821b799cef..1e780f9ef84 100644
--- a/app/assets/javascripts/boards/components/board_card.vue
+++ b/app/assets/javascripts/boards/components/board_card.vue
@@ -1,5 +1,6 @@
<script>
import { mapActions, mapState } from 'vuex';
+import Tracking from '~/tracking';
import BoardCardInner from './board_card_inner.vue';
export default {
@@ -7,6 +8,7 @@ export default {
components: {
BoardCardInner,
},
+ mixins: [Tracking.mixin()],
props: {
list: {
type: Object,
@@ -40,6 +42,12 @@ export default {
this.selectedBoardItems.findIndex((boardItem) => boardItem.id === this.item.id) > -1
);
},
+ isDisabled() {
+ return this.disabled || !this.item.id || this.item.isLoading;
+ },
+ isDraggable() {
+ return !this.disabled && this.item.id && !this.item.isLoading;
+ },
},
methods: {
...mapActions(['toggleBoardItemMultiSelection', 'toggleBoardItem']),
@@ -48,10 +56,11 @@ export default {
if (e.target.closest('.js-no-trigger')) return;
const isMultiSelect = e.ctrlKey || e.metaKey;
- if (isMultiSelect) {
+ if (isMultiSelect && gon?.features?.boardMultiSelect) {
this.toggleBoardItemMultiSelection(this.item);
} else {
this.toggleBoardItem({ boardItem: this.item });
+ this.track('click_card', { label: 'right_sidebar' });
}
},
},
@@ -63,9 +72,10 @@ export default {
data-qa-selector="board_card"
:class="{
'multi-select': multiSelectVisible,
- 'user-can-drag': !disabled && item.id,
- 'is-disabled': disabled || !item.id,
+ 'user-can-drag': isDraggable,
+ 'is-disabled': isDisabled,
'is-active': isActive,
+ 'gl-cursor-not-allowed gl-bg-gray-10': item.isLoading,
}"
:index="index"
:data-item-id="item.id"
diff --git a/app/assets/javascripts/boards/components/board_card_inner.vue b/app/assets/javascripts/boards/components/board_card_inner.vue
index 0cb2e64042e..2f4e9044b9e 100644
--- a/app/assets/javascripts/boards/components/board_card_inner.vue
+++ b/app/assets/javascripts/boards/components/board_card_inner.vue
@@ -1,5 +1,5 @@
<script>
-import { GlLabel, GlTooltipDirective, GlIcon } from '@gitlab/ui';
+import { GlLabel, GlTooltipDirective, GlIcon, GlLoadingIcon } from '@gitlab/ui';
import { sortBy } from 'lodash';
import { mapActions, mapGetters, mapState } from 'vuex';
import boardCardInner from 'ee_else_ce/boards/mixins/board_card_inner';
@@ -17,6 +17,7 @@ import IssueTimeEstimate from './issue_time_estimate.vue';
export default {
components: {
GlLabel,
+ GlLoadingIcon,
GlIcon,
UserAvatarLink,
TooltipOnTruncate,
@@ -181,9 +182,13 @@ export default {
class="confidential-icon gl-mr-2"
:aria-label="__('Confidential')"
/>
- <a :href="item.path || item.webUrl || ''" :title="item.title" @mousemove.stop>{{
- item.title
- }}</a>
+ <a
+ :href="item.path || item.webUrl || ''"
+ :title="item.title"
+ :class="{ 'gl-text-gray-400!': item.isLoading }"
+ @mousemove.stop
+ >{{ item.title }}</a
+ >
</h4>
</div>
<div v-if="showLabelFooter" class="board-card-labels gl-mt-2 gl-display-flex gl-flex-wrap">
@@ -206,6 +211,7 @@ export default {
<div
class="gl-display-flex align-items-start flex-wrap-reverse board-card-number-container gl-overflow-hidden js-board-card-number-container"
>
+ <gl-loading-icon v-if="item.isLoading" size="md" class="mt-3" />
<span
v-if="item.referencePath"
class="board-card-number gl-overflow-hidden gl-display-flex gl-mr-3 gl-mt-3"
diff --git a/app/assets/javascripts/boards/components/board_column.vue b/app/assets/javascripts/boards/components/board_column.vue
index c9e667d526c..cc7262f3a39 100644
--- a/app/assets/javascripts/boards/components/board_column.vue
+++ b/app/assets/javascripts/boards/components/board_column.vue
@@ -24,11 +24,6 @@ export default {
type: Boolean,
required: true,
},
- canAdminList: {
- type: Boolean,
- required: false,
- default: false,
- },
},
computed: {
...mapState(['filterParams', 'highlightedLists']),
@@ -92,14 +87,8 @@ export default {
class="board-inner gl-display-flex gl-flex-direction-column gl-relative gl-h-full gl-rounded-base"
:class="{ 'board-column-highlighted': highlighted }"
>
- <board-list-header :can-admin-list="canAdminList" :list="list" :disabled="disabled" />
- <board-list
- ref="board-list"
- :disabled="disabled"
- :board-items="listItems"
- :list="list"
- :can-admin-list="canAdminList"
- />
+ <board-list-header :list="list" :disabled="disabled" />
+ <board-list ref="board-list" :disabled="disabled" :board-items="listItems" :list="list" />
</div>
</div>
</template>
diff --git a/app/assets/javascripts/boards/components/board_column_deprecated.vue b/app/assets/javascripts/boards/components/board_column_deprecated.vue
index 3dc77654e28..7c090dfaa53 100644
--- a/app/assets/javascripts/boards/components/board_column_deprecated.vue
+++ b/app/assets/javascripts/boards/components/board_column_deprecated.vue
@@ -26,11 +26,6 @@ export default {
type: Boolean,
required: true,
},
- canAdminList: {
- type: Boolean,
- required: false,
- default: false,
- },
},
data() {
return {
@@ -110,7 +105,7 @@ export default {
class="board-inner gl-display-flex gl-flex-direction-column gl-relative gl-h-full gl-rounded-base"
:class="{ 'board-column-highlighted': list.highlighted }"
>
- <board-list-header :can-admin-list="canAdminList" :list="list" :disabled="disabled" />
+ <board-list-header :list="list" :disabled="disabled" />
<board-list ref="board-list" :disabled="disabled" :issues="listIssues" :list="list" />
</div>
</div>
diff --git a/app/assets/javascripts/boards/components/board_content.vue b/app/assets/javascripts/boards/components/board_content.vue
index b8a38d833ad..b770ac06e89 100644
--- a/app/assets/javascripts/boards/components/board_content.vue
+++ b/app/assets/javascripts/boards/components/board_content.vue
@@ -106,7 +106,6 @@ export default {
v-for="(list, index) in boardListsToUse"
:key="index"
ref="board"
- :can-admin-list="canAdminList"
:list="list"
:disabled="disabled"
/>
diff --git a/app/assets/javascripts/boards/components/board_content_sidebar.vue b/app/assets/javascripts/boards/components/board_content_sidebar.vue
index e1f8457c0e2..16a8a9d253f 100644
--- a/app/assets/javascripts/boards/components/board_content_sidebar.vue
+++ b/app/assets/javascripts/boards/components/board_content_sidebar.vue
@@ -1,16 +1,17 @@
<script>
import { GlDrawer } from '@gitlab/ui';
import { mapState, mapActions, mapGetters } from 'vuex';
-import BoardSidebarDueDate from '~/boards/components/sidebar/board_sidebar_due_date.vue';
+import SidebarDropdownWidget from 'ee_else_ce/sidebar/components/sidebar_dropdown_widget.vue';
import BoardSidebarLabelsSelect from '~/boards/components/sidebar/board_sidebar_labels_select.vue';
-import BoardSidebarMilestoneSelect from '~/boards/components/sidebar/board_sidebar_milestone_select.vue';
import BoardSidebarTimeTracker from '~/boards/components/sidebar/board_sidebar_time_tracker.vue';
import BoardSidebarTitle from '~/boards/components/sidebar/board_sidebar_title.vue';
import { ISSUABLE } from '~/boards/constants';
import { contentTop } from '~/lib/utils/common_utils';
import SidebarAssigneesWidget from '~/sidebar/components/assignees/sidebar_assignees_widget.vue';
import SidebarConfidentialityWidget from '~/sidebar/components/confidential/sidebar_confidentiality_widget.vue';
+import SidebarDateWidget from '~/sidebar/components/date/sidebar_date_widget.vue';
import SidebarSubscriptionsWidget from '~/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
headerHeight: `${contentTop()}px`,
@@ -18,19 +19,18 @@ export default {
GlDrawer,
BoardSidebarTitle,
SidebarAssigneesWidget,
+ SidebarDateWidget,
SidebarConfidentialityWidget,
BoardSidebarTimeTracker,
BoardSidebarLabelsSelect,
- BoardSidebarDueDate,
SidebarSubscriptionsWidget,
- BoardSidebarMilestoneSelect,
- BoardSidebarEpicSelect: () =>
- import('ee_component/boards/components/sidebar/board_sidebar_epic_select.vue'),
+ SidebarDropdownWidget,
BoardSidebarWeightInput: () =>
import('ee_component/boards/components/sidebar/board_sidebar_weight_input.vue'),
- SidebarIterationWidget: () =>
- import('ee_component/sidebar/components/sidebar_iteration_widget.vue'),
+ IterationSidebarDropdownWidget: () =>
+ import('ee_component/sidebar/components/iteration_sidebar_dropdown_widget.vue'),
},
+ mixins: [glFeatureFlagMixin()],
inject: {
multipleAssigneesFeatureAvailable: {
default: false,
@@ -89,20 +89,57 @@ export default {
:allow-multiple-assignees="multipleAssigneesFeatureAvailable"
@assignees-updated="setAssignees"
/>
- <board-sidebar-epic-select v-if="epicFeatureAvailable" class="epic" />
+ <sidebar-dropdown-widget
+ v-if="epicFeatureAvailable"
+ :iid="activeBoardItem.iid"
+ issuable-attribute="epic"
+ :workspace-path="projectPathForActiveIssue"
+ :attr-workspace-path="groupPathForActiveIssue"
+ :issuable-type="issuableType"
+ data-testid="sidebar-epic"
+ />
<div>
- <board-sidebar-milestone-select />
- <sidebar-iteration-widget
- v-if="iterationFeatureAvailable"
+ <sidebar-dropdown-widget
:iid="activeBoardItem.iid"
+ issuable-attribute="milestone"
:workspace-path="projectPathForActiveIssue"
- :iterations-workspace-path="groupPathForActiveIssue"
+ :attr-workspace-path="projectPathForActiveIssue"
:issuable-type="issuableType"
- class="gl-mt-5"
+ data-testid="sidebar-milestones"
/>
+ <template v-if="!glFeatures.iterationCadences">
+ <sidebar-dropdown-widget
+ v-if="iterationFeatureAvailable"
+ :iid="activeBoardItem.iid"
+ issuable-attribute="iteration"
+ :workspace-path="projectPathForActiveIssue"
+ :attr-workspace-path="groupPathForActiveIssue"
+ :issuable-type="issuableType"
+ class="gl-mt-5"
+ data-testid="iteration-edit"
+ data-qa-selector="iteration_container"
+ />
+ </template>
+ <template v-else>
+ <iteration-sidebar-dropdown-widget
+ v-if="iterationFeatureAvailable"
+ :iid="activeBoardItem.iid"
+ :workspace-path="projectPathForActiveIssue"
+ :attr-workspace-path="groupPathForActiveIssue"
+ :issuable-type="issuableType"
+ class="gl-mt-5"
+ data-testid="iteration-edit"
+ data-qa-selector="iteration_container"
+ />
+ </template>
</div>
<board-sidebar-time-tracker class="swimlanes-sidebar-time-tracker" />
- <board-sidebar-due-date />
+ <sidebar-date-widget
+ :iid="activeBoardItem.iid"
+ :full-path="fullPath"
+ :issuable-type="issuableType"
+ data-testid="sidebar-due-date"
+ />
<board-sidebar-labels-select class="labels" />
<board-sidebar-weight-input v-if="weightFeatureAvailable" class="weight" />
<sidebar-confidentiality-widget
diff --git a/app/assets/javascripts/boards/components/board_filtered_search.vue b/app/assets/javascripts/boards/components/board_filtered_search.vue
index e564af0c353..13388f02f1f 100644
--- a/app/assets/javascripts/boards/components/board_filtered_search.vue
+++ b/app/assets/javascripts/boards/components/board_filtered_search.vue
@@ -3,6 +3,7 @@ import { pickBy } from 'lodash';
import { mapActions } from 'vuex';
import { updateHistory, setUrlParams } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
+import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
import FilteredSearch from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
export default {
@@ -104,7 +105,9 @@ export default {
},
getFilterParams(filters = []) {
const notFilters = filters.filter((item) => item.value.operator === '!=');
- const equalsFilters = filters.filter((item) => item.value.operator === '=');
+ const equalsFilters = filters.filter(
+ (item) => item?.value?.operator === '=' || item.type === FILTERED_SEARCH_TERM,
+ );
return { ...this.generateParams(equalsFilters), not: { ...this.generateParams(notFilters) } };
},
diff --git a/app/assets/javascripts/boards/components/board_form.vue b/app/assets/javascripts/boards/components/board_form.vue
index 78da4137d69..aa75a0d68f5 100644
--- a/app/assets/javascripts/boards/components/board_form.vue
+++ b/app/assets/javascripts/boards/components/board_form.vue
@@ -1,7 +1,7 @@
<script>
-import { GlModal } from '@gitlab/ui';
-import { mapGetters } from 'vuex';
-import { deprecatedCreateFlash as Flash } from '~/flash';
+import { GlModal, GlAlert } from '@gitlab/ui';
+import { mapGetters, mapActions, mapState } from 'vuex';
+import ListLabel from '~/boards/models/label';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import { getParameterByName } from '~/lib/utils/common_utils';
import { visitUrl } from '~/lib/utils/url_utility';
@@ -44,6 +44,7 @@ export default {
BoardScope: () => import('ee_component/boards/components/board_scope.vue'),
GlModal,
BoardConfigurationOptions,
+ GlAlert,
},
inject: {
fullPath: {
@@ -107,6 +108,7 @@ export default {
};
},
computed: {
+ ...mapState(['error']),
...mapGetters(['isIssueBoard', 'isGroupBoard', 'isProjectBoard']),
isNewForm() {
return this.currentPage === formType.new;
@@ -222,9 +224,7 @@ export default {
}
},
methods: {
- setIteration(iterationId) {
- this.board.iteration_id = iterationId;
- },
+ ...mapActions(['setError', 'unsetError']),
boardCreateResponse(data) {
return data.createBoard.board.webPath;
},
@@ -235,6 +235,9 @@ export default {
: '';
return `${path}${param}`;
},
+ cancel() {
+ this.$emit('cancel');
+ },
async createOrUpdateBoard() {
const response = await this.$apollo.mutate({
mutation: this.currentMutation,
@@ -263,7 +266,7 @@ export default {
await this.deleteBoard();
visitUrl(this.rootPath);
} catch {
- Flash(this.$options.i18n.deleteErrorMessage);
+ this.setError({ message: this.$options.i18n.deleteErrorMessage });
} finally {
this.isLoading = false;
}
@@ -272,15 +275,12 @@ export default {
const url = await this.createOrUpdateBoard();
visitUrl(url);
} catch {
- Flash(this.$options.i18n.saveErrorMessage);
+ this.setError({ message: this.$options.i18n.saveErrorMessage });
} finally {
this.isLoading = false;
}
}
},
- cancel() {
- this.$emit('cancel');
- },
resetFormState() {
if (this.isNewForm) {
// Clear the form when we open the "New board" modal
@@ -289,6 +289,25 @@ export default {
this.board = { ...boardDefaults, ...this.currentBoard };
}
},
+ setIteration(iterationId) {
+ 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(
+ new ListLabel({
+ id: label.id,
+ title: label.title,
+ color: label.color,
+ textColor: label.text_color,
+ }),
+ );
+ } else if (!label.set) {
+ this.board.labels = this.board.labels.filter((selected) => selected.id !== label.id);
+ }
+ });
+ },
},
};
</script>
@@ -308,6 +327,15 @@ export default {
@close="cancel"
@hide.prevent
>
+ <gl-alert
+ v-if="error"
+ class="gl-mb-3"
+ variant="danger"
+ :dismissible="true"
+ @dismiss="unsetError"
+ >
+ {{ error }}
+ </gl-alert>
<p v-if="isDeleteForm" data-testid="delete-confirmation-message">
{{ $options.i18n.deleteConfirmationMessage }}
</p>
@@ -346,6 +374,7 @@ export default {
:group-id="groupId"
:weights="weights"
@set-iteration="setIteration"
+ @set-board-labels="setBoardLabels"
/>
</form>
</gl-modal>
diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue
index 94e29f3ad86..81740b5cd17 100644
--- a/app/assets/javascripts/boards/components/board_list.vue
+++ b/app/assets/javascripts/boards/components/board_list.vue
@@ -1,10 +1,11 @@
<script>
-import { GlLoadingIcon } from '@gitlab/ui';
+import { GlLoadingIcon, GlIntersectionObserver } from '@gitlab/ui';
import Draggable from 'vuedraggable';
import { mapActions, mapGetters, mapState } from 'vuex';
import { sortableStart, sortableEnd } from '~/boards/mixins/sortable_default_options';
import { sprintf, __ } from '~/locale';
import defaultSortableConfig from '~/sortable/sortable_config';
+import Tracking from '~/tracking';
import eventHub from '../eventhub';
import BoardCard from './board_card.vue';
import BoardNewIssue from './board_new_issue.vue';
@@ -21,6 +22,13 @@ export default {
BoardCard,
BoardNewIssue,
GlLoadingIcon,
+ GlIntersectionObserver,
+ },
+ mixins: [Tracking.mixin()],
+ inject: {
+ canAdminList: {
+ default: false,
+ },
},
props: {
disabled: {
@@ -35,11 +43,6 @@ export default {
type: Array,
required: true,
},
- canAdminList: {
- type: Boolean,
- required: false,
- default: false,
- },
},
data() {
return {
@@ -65,7 +68,7 @@ export default {
return this.list.maxIssueCount > 0 && this.listItemsCount > this.list.maxIssueCount;
},
hasNextPage() {
- return this.pageInfoByListId[this.list.id].hasNextPage;
+ return this.pageInfoByListId[this.list.id]?.hasNextPage;
},
loading() {
return this.listsFlags[this.list.id]?.isLoading;
@@ -86,7 +89,9 @@ export default {
: this.$options.i18n.showingAllIssues;
},
treeRootWrapper() {
- return this.canAdminList ? Draggable : 'ul';
+ return this.canAdminList && !this.listsFlags[this.list.id]?.addItemToListInProgress
+ ? Draggable
+ : 'ul';
},
treeRootOptions() {
const options = {
@@ -108,19 +113,21 @@ export default {
this.showCount = this.scrollHeight() > Math.ceil(this.listHeight());
});
},
- },
- created() {
- eventHub.$on(`toggle-issue-form-${this.list.id}`, this.toggleForm);
- eventHub.$on(`scroll-board-list-${this.list.id}`, this.scrollToTop);
- },
- mounted() {
- // Scroll event on list to load more
- this.listRef.addEventListener('scroll', this.onScroll);
+ 'list.id': {
+ handler(id, oldVal) {
+ if (id) {
+ eventHub.$on(`toggle-issue-form-${this.list.id}`, this.toggleForm);
+ eventHub.$on(`scroll-board-list-${this.list.id}`, this.scrollToTop);
+ eventHub.$off(`toggle-issue-form-${oldVal}`, this.toggleForm);
+ eventHub.$off(`scroll-board-list-${oldVal}`, this.scrollToTop);
+ }
+ },
+ immediate: true,
+ },
},
beforeDestroy() {
eventHub.$off(`toggle-issue-form-${this.list.id}`, this.toggleForm);
eventHub.$off(`scroll-board-list-${this.list.id}`, this.scrollToTop);
- this.listRef.removeEventListener('scroll', this.onScroll);
},
methods: {
...mapActions(['fetchItemsForList', 'moveItem']),
@@ -142,28 +149,31 @@ export default {
toggleForm() {
this.showIssueForm = !this.showIssueForm;
},
- onScroll() {
- window.requestAnimationFrame(() => {
- if (
- !this.loadingMore &&
- this.scrollTop() > this.scrollHeight() - this.scrollOffset &&
- this.hasNextPage
- ) {
- this.loadNextPage();
- }
- });
+ onReachingListBottom() {
+ if (!this.loadingMore && this.hasNextPage) {
+ this.showCount = true;
+ this.loadNextPage();
+ }
},
handleDragOnStart() {
sortableStart();
+ this.track('drag_card', { label: 'board' });
},
handleDragOnEnd(params) {
sortableEnd();
- const { newIndex, oldIndex, from, to, item } = params;
+ const { oldIndex, from, to, item } = params;
+ let { newIndex } = params;
const { itemId, itemIid, itemPath } = item.dataset;
- const { children } = to;
+ let { children } = to;
let moveBeforeId;
let moveAfterId;
+ children = Array.from(children).filter((card) => card.classList.contains('board-card'));
+
+ if (newIndex > children.length) {
+ newIndex = children.length;
+ }
+
const getItemId = (el) => Number(el.dataset.itemId);
// If item is being moved within the same list
@@ -226,6 +236,7 @@ export default {
:data-board="list.id"
:data-board-type="list.listType"
:class="{ 'bg-danger-100': boardItemsSizeExceedsMax }"
+ draggable=".board-card"
class="board-list gl-w-full gl-h-full gl-list-style-none gl-mb-0 gl-p-2 js-board-list"
data-testid="tree-root-wrapper"
@start="handleDragOnStart"
@@ -240,15 +251,17 @@ export default {
:item="item"
:disabled="disabled"
/>
- <li v-if="showCount" class="board-list-count gl-text-center" data-issue-id="-1">
- <gl-loading-icon
- v-if="loadingMore"
- :label="$options.i18n.loadingMoreboardItems"
- data-testid="count-loading-icon"
- />
- <span v-if="showingAllItems">{{ showingAllItemsText }}</span>
- <span v-else>{{ paginatedIssueText }}</span>
- </li>
+ <gl-intersection-observer @appear="onReachingListBottom">
+ <li v-if="showCount" class="board-list-count gl-text-center" data-issue-id="-1">
+ <gl-loading-icon
+ v-if="loadingMore"
+ :label="$options.i18n.loadingMoreboardItems"
+ data-testid="count-loading-icon"
+ />
+ <span v-if="showingAllItems">{{ showingAllItemsText }}</span>
+ <span v-else>{{ paginatedIssueText }}</span>
+ </li>
+ </gl-intersection-observer>
</component>
</div>
</template>
diff --git a/app/assets/javascripts/boards/components/board_list_deprecated.vue b/app/assets/javascripts/boards/components/board_list_deprecated.vue
index 0534e027c86..9b3e7e1547d 100644
--- a/app/assets/javascripts/boards/components/board_list_deprecated.vue
+++ b/app/assets/javascripts/boards/components/board_list_deprecated.vue
@@ -1,7 +1,7 @@
<script>
import { GlLoadingIcon } from '@gitlab/ui';
import { Sortable, MultiDrag } from 'sortablejs';
-import { deprecatedCreateFlash as createFlash } from '~/flash';
+import createFlash from '~/flash';
import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants';
import { sprintf, __ } from '~/locale';
import eventHub from '../eventhub';
@@ -91,6 +91,13 @@ export default {
}
});
},
+ 'list.id': {
+ handler(id) {
+ if (id) {
+ eventHub.$on(`toggle-issue-form-${this.list.id}`, this.toggleForm);
+ }
+ },
+ },
},
created() {
eventHub.$on(`toggle-issue-form-${this.list.id}`, this.toggleForm);
@@ -295,7 +302,9 @@ export default {
}
if (!toList) {
- createFlash(__('Something went wrong while performing the action.'));
+ createFlash({
+ message: __('Something went wrong while performing the action.'),
+ });
}
if (!isSameList) {
diff --git a/app/assets/javascripts/boards/components/board_list_header.vue b/app/assets/javascripts/boards/components/board_list_header.vue
index f94697172ac..bf8396f52a6 100644
--- a/app/assets/javascripts/boards/components/board_list_header.vue
+++ b/app/assets/javascripts/boards/components/board_list_header.vue
@@ -14,6 +14,7 @@ import { isScopedLabel, parseBoolean } from '~/lib/utils/common_utils';
import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants';
import { n__, s__, __ } from '~/locale';
import sidebarEventHub from '~/sidebar/event_hub';
+import Tracking from '~/tracking';
import AccessorUtilities from '../../lib/utils/accessor';
import { inactiveId, LIST, ListType } from '../constants';
import eventHub from '../eventhub';
@@ -38,6 +39,7 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
+ mixins: [Tracking.mixin()],
inject: {
boardId: {
default: '',
@@ -98,6 +100,12 @@ export default {
showListDetails() {
return !this.list.collapsed || !this.isSwimlanesHeader;
},
+ showListHeaderActions() {
+ if (this.isLoggedIn) {
+ return this.isNewIssueShown || this.isSettingsShown;
+ }
+ return false;
+ },
itemsCount() {
return this.list.issuesCount;
},
@@ -149,6 +157,8 @@ export default {
}
this.setActiveId({ id: this.list.id, sidebarType: LIST });
+
+ this.track('click_button', { label: 'list_settings' });
},
showScopedLabels(label) {
return this.scopedLabelsAvailable && isScopedLabel(label);
@@ -170,6 +180,11 @@ export default {
// When expanding/collapsing, the tooltip on the caret button sometimes stays open.
// Close all tooltips manually to prevent dangling tooltips.
this.$root.$emit(BV_HIDE_TOOLTIP);
+
+ this.track('click_toggle_button', {
+ label: 'toggle_list',
+ property: collapsed ? 'closed' : 'open',
+ });
},
addToLocalStorage() {
if (AccessorUtilities.isLocalStorageAccessSafe()) {
@@ -351,10 +366,7 @@ export default {
<!-- EE end -->
</span>
</div>
- <gl-button-group
- v-if="isNewIssueShown || isSettingsShown"
- class="board-list-button-group pl-2"
- >
+ <gl-button-group v-if="showListHeaderActions" class="board-list-button-group gl-pl-2">
<gl-button
v-if="isNewIssueShown"
v-show="!list.collapsed"
diff --git a/app/assets/javascripts/boards/components/board_list_header_deprecated.vue b/app/assets/javascripts/boards/components/board_list_header_deprecated.vue
index 429ffd4cd06..bc29728fc55 100644
--- a/app/assets/javascripts/boards/components/board_list_header_deprecated.vue
+++ b/app/assets/javascripts/boards/components/board_list_header_deprecated.vue
@@ -35,6 +35,9 @@ export default {
GlTooltip: GlTooltipDirective,
},
inject: {
+ currentUserId: {
+ default: null,
+ },
boardId: {
default: '',
},
@@ -63,7 +66,7 @@ export default {
computed: {
...mapState(['activeId']),
isLoggedIn() {
- return Boolean(gon.current_user_id);
+ return Boolean(this.currentUserId);
},
listType() {
return this.list.type;
@@ -89,6 +92,12 @@ export default {
showListDetails() {
return this.list.isExpanded || !this.isSwimlanesHeader;
},
+ showListHeaderActions() {
+ if (this.isLoggedIn) {
+ return this.isNewIssueShown || this.isSettingsShown;
+ }
+ return false;
+ },
issuesCount() {
return this.list.issuesSize;
},
@@ -320,10 +329,7 @@ export default {
</template>
</span>
</div>
- <gl-button-group
- v-if="isNewIssueShown || isSettingsShown"
- class="board-list-button-group pl-2"
- >
+ <gl-button-group v-if="showListHeaderActions" class="board-list-button-group pl-2">
<gl-button
v-if="isNewIssueShown"
ref="newIssueBtn"
diff --git a/app/assets/javascripts/boards/components/board_new_issue.vue b/app/assets/javascripts/boards/components/board_new_issue.vue
index 144cae15ab3..a63b49f9508 100644
--- a/app/assets/javascripts/boards/components/board_new_issue.vue
+++ b/app/assets/javascripts/boards/components/board_new_issue.vue
@@ -102,7 +102,7 @@ export default {
ref="submitButton"
:disabled="disabled"
class="float-left js-no-auto-disable"
- variant="success"
+ variant="confirm"
category="primary"
type="submit"
>
diff --git a/app/assets/javascripts/boards/components/board_settings_sidebar.vue b/app/assets/javascripts/boards/components/board_settings_sidebar.vue
index 3d7f1f38a34..75975c77df5 100644
--- a/app/assets/javascripts/boards/components/board_settings_sidebar.vue
+++ b/app/assets/javascripts/boards/components/board_settings_sidebar.vue
@@ -6,6 +6,7 @@ import boardsStore from '~/boards/stores/boards_store';
import { isScopedLabel } from '~/lib/utils/common_utils';
import { __ } from '~/locale';
import eventHub from '~/sidebar/event_hub';
+import Tracking from '~/tracking';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
// NOTE: need to revisit how we handle headerHeight, because we have so many different header and footer options.
@@ -21,7 +22,7 @@ export default {
BoardSettingsListTypes: () =>
import('ee_component/boards/components/board_settings_list_types.vue'),
},
- mixins: [glFeatureFlagMixin()],
+ mixins: [glFeatureFlagMixin(), Tracking.mixin()],
inject: ['canAdminList'],
data() {
return {
@@ -72,6 +73,7 @@ export default {
// eslint-disable-next-line no-alert
if (window.confirm(__('Are you sure you want to remove this list?'))) {
if (this.shouldUseGraphQL || this.isEpicBoard) {
+ this.track('click_button', { label: 'remove_list' });
this.removeList(this.activeId);
} else {
this.activeList.destroy();
diff --git a/app/assets/javascripts/boards/components/config_toggle.vue b/app/assets/javascripts/boards/components/config_toggle.vue
index fdb60d0ae6a..30e304b8a65 100644
--- a/app/assets/javascripts/boards/components/config_toggle.vue
+++ b/app/assets/javascripts/boards/components/config_toggle.vue
@@ -3,6 +3,7 @@ import { GlButton, GlModalDirective, GlTooltipDirective } from '@gitlab/ui';
import { formType } from '~/boards/constants';
import eventHub from '~/boards/eventhub';
import { s__, __ } from '~/locale';
+import Tracking from '~/tracking';
export default {
components: {
@@ -12,6 +13,7 @@ export default {
GlTooltip: GlTooltipDirective,
GlModalDirective,
},
+ mixins: [Tracking.mixin()],
props: {
boardsStore: {
type: Object,
@@ -37,6 +39,7 @@ export default {
},
methods: {
showPage() {
+ this.track('click_button', { label: 'edit_board' });
eventHub.$emit('showBoardModal', formType.edit);
if (this.boardsStore) {
this.boardsStore.showPage(formType.edit);
diff --git a/app/assets/javascripts/boards/components/issue_card_inner_deprecated.vue b/app/assets/javascripts/boards/components/issue_card_inner_deprecated.vue
index 2652fac1818..6e90731cc2f 100644
--- a/app/assets/javascripts/boards/components/issue_card_inner_deprecated.vue
+++ b/app/assets/javascripts/boards/components/issue_card_inner_deprecated.vue
@@ -95,6 +95,9 @@ export default {
}
return __('Blocked issue');
},
+ assignees() {
+ return this.issue.assignees.filter((_, index) => this.shouldRenderAssignee(index));
+ },
},
methods: {
isIndexLessThanlimit(index) {
@@ -215,8 +218,7 @@ export default {
</div>
<div class="board-card-assignee gl-display-flex">
<user-avatar-link
- v-for="(assignee, index) in issue.assignees"
- v-if="shouldRenderAssignee(index)"
+ v-for="assignee in assignees"
:key="assignee.id"
:link-href="assigneeUrl(assignee)"
:img-alt="avatarUrlTitle(assignee)"
diff --git a/app/assets/javascripts/boards/components/issue_time_estimate_deprecated.vue b/app/assets/javascripts/boards/components/issue_time_estimate_deprecated.vue
index fe56833016e..8ddf50cb357 100644
--- a/app/assets/javascripts/boards/components/issue_time_estimate_deprecated.vue
+++ b/app/assets/javascripts/boards/components/issue_time_estimate_deprecated.vue
@@ -10,7 +10,7 @@ export default {
},
props: {
estimate: {
- type: Number,
+ type: [Number, String],
required: true,
},
},
diff --git a/app/assets/javascripts/boards/components/sidebar/board_sidebar_due_date.vue b/app/assets/javascripts/boards/components/sidebar/board_sidebar_due_date.vue
deleted file mode 100644
index 13e1e232676..00000000000
--- a/app/assets/javascripts/boards/components/sidebar/board_sidebar_due_date.vue
+++ /dev/null
@@ -1,110 +0,0 @@
-<script>
-import { GlButton, GlDatepicker } from '@gitlab/ui';
-import { mapGetters, mapActions } from 'vuex';
-import BoardEditableItem from '~/boards/components/sidebar/board_editable_item.vue';
-import createFlash from '~/flash';
-import { dateInWords, formatDate, parsePikadayDate } from '~/lib/utils/datetime_utility';
-import { __ } from '~/locale';
-
-export default {
- components: {
- BoardEditableItem,
- GlButton,
- GlDatepicker,
- },
- data() {
- return {
- loading: false,
- };
- },
- computed: {
- ...mapGetters(['activeBoardItem', 'projectPathForActiveIssue']),
- hasDueDate() {
- return this.activeBoardItem.dueDate != null;
- },
- parsedDueDate() {
- if (!this.hasDueDate) {
- return null;
- }
-
- return parsePikadayDate(this.activeBoardItem.dueDate);
- },
- formattedDueDate() {
- if (!this.hasDueDate) {
- return '';
- }
-
- return dateInWords(this.parsedDueDate, true);
- },
- },
- methods: {
- ...mapActions(['setActiveIssueDueDate']),
- async openDatePicker() {
- await this.$nextTick();
- this.$refs.datePicker.calendar.show();
- },
- async setDueDate(date) {
- this.loading = true;
- this.$refs.sidebarItem.collapse();
-
- try {
- const dueDate = date ? formatDate(date, 'yyyy-mm-dd') : null;
- await this.setActiveIssueDueDate({ dueDate, projectPath: this.projectPathForActiveIssue });
- } catch (e) {
- createFlash({ message: this.$options.i18n.updateDueDateError });
- } finally {
- this.loading = false;
- }
- },
- },
- i18n: {
- dueDate: __('Due date'),
- removeDueDate: __('remove due date'),
- updateDueDateError: __('An error occurred when updating the issue due date'),
- },
-};
-</script>
-
-<template>
- <board-editable-item
- ref="sidebarItem"
- class="board-sidebar-due-date"
- data-testid="sidebar-due-date"
- :title="$options.i18n.dueDate"
- :loading="loading"
- @open="openDatePicker"
- >
- <template v-if="hasDueDate" #collapsed>
- <div class="gl-display-flex gl-align-items-center">
- <strong class="gl-text-gray-900">{{ formattedDueDate }}</strong>
- <span class="gl-mx-2">-</span>
- <gl-button
- variant="link"
- class="gl-text-gray-500!"
- data-testid="reset-button"
- :disabled="loading"
- @click="setDueDate(null)"
- >
- {{ $options.i18n.removeDueDate }}
- </gl-button>
- </div>
- </template>
- <gl-datepicker
- ref="datePicker"
- :value="parsedDueDate"
- show-clear-button
- @input="setDueDate"
- @clear="setDueDate(null)"
- />
- </board-editable-item>
-</template>
-<style>
-/*
- * This can be removed after closing:
- * https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1048
- */
-.board-sidebar-due-date .gl-datepicker,
-.board-sidebar-due-date .gl-datepicker-input {
- width: 100%;
-}
-</style>
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 919ef0d3783..29febd0fa51 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
@@ -3,7 +3,6 @@ import { GlLabel } from '@gitlab/ui';
import { mapGetters, mapActions } from 'vuex';
import Api from '~/api';
import BoardEditableItem from '~/boards/components/sidebar/board_editable_item.vue';
-import createFlash from '~/flash';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { isScopedLabel } from '~/lib/utils/common_utils';
import { mergeUrlParams } from '~/lib/utils/url_utility';
@@ -50,10 +49,10 @@ export default {
/*
Labels fetched in epic boards are always group-level labels
and the correct path are passed from the backend (injected through labelsFetchPath)
-
+
For issue boards, we should always include project-level labels and use a different endpoint.
(it requires knowing the project path of a selected issue.)
-
+
Note 1. that we will be using GraphQL to fetch labels when we create a labels select widget.
And this component will be removed _wholesale_ https://gitlab.com/gitlab-org/gitlab/-/issues/300653.
@@ -74,7 +73,7 @@ export default {
},
},
methods: {
- ...mapActions(['setActiveBoardItemLabels']),
+ ...mapActions(['setActiveBoardItemLabels', 'setError']),
async setLabels(payload) {
this.loading = true;
this.$refs.sidebarItem.collapse();
@@ -88,7 +87,7 @@ export default {
const input = { addLabelIds, removeLabelIds, projectPath: this.projectPathForActiveIssue };
await this.setActiveBoardItemLabels(input);
} catch (e) {
- createFlash({ message: __('An error occurred while updating labels.') });
+ this.setError({ error: e, message: __('An error occurred while updating labels.') });
} finally {
this.loading = false;
}
@@ -101,7 +100,7 @@ export default {
const input = { removeLabelIds, projectPath: this.projectPathForActiveIssue };
await this.setActiveBoardItemLabels(input);
} catch (e) {
- createFlash({ message: __('An error occurred when removing the label.') });
+ this.setError({ error: e, message: __('An error occurred when removing the label.') });
} finally {
this.loading = false;
}
diff --git a/app/assets/javascripts/boards/components/sidebar/board_sidebar_milestone_select.vue b/app/assets/javascripts/boards/components/sidebar/board_sidebar_milestone_select.vue
deleted file mode 100644
index ad225c7bf5c..00000000000
--- a/app/assets/javascripts/boards/components/sidebar/board_sidebar_milestone_select.vue
+++ /dev/null
@@ -1,158 +0,0 @@
-<script>
-import {
- GlDropdown,
- GlDropdownItem,
- GlDropdownText,
- GlSearchBoxByType,
- GlDropdownDivider,
- GlLoadingIcon,
-} from '@gitlab/ui';
-import { mapGetters, mapActions } from 'vuex';
-import BoardEditableItem from '~/boards/components/sidebar/board_editable_item.vue';
-import createFlash from '~/flash';
-import { __, s__ } from '~/locale';
-import projectMilestones from '../../graphql/project_milestones.query.graphql';
-
-export default {
- components: {
- BoardEditableItem,
- GlDropdown,
- GlLoadingIcon,
- GlDropdownItem,
- GlDropdownText,
- GlSearchBoxByType,
- GlDropdownDivider,
- },
- data() {
- return {
- milestones: [],
- searchTitle: '',
- loading: false,
- edit: false,
- };
- },
- apollo: {
- milestones: {
- query: projectMilestones,
- debounce: 250,
- skip() {
- return !this.edit;
- },
- variables() {
- return {
- fullPath: this.projectPath,
- searchTitle: this.searchTitle,
- state: 'active',
- includeAncestors: true,
- };
- },
- update(data) {
- const edges = data?.project?.milestones?.edges ?? [];
- return edges.map((item) => item.node);
- },
- error() {
- createFlash({ message: this.$options.i18n.fetchMilestonesError });
- },
- },
- },
- computed: {
- ...mapGetters(['activeBoardItem']),
- hasMilestone() {
- return this.activeBoardItem.milestone !== null;
- },
- groupFullPath() {
- const { referencePath = '' } = this.activeBoardItem;
- return referencePath.slice(0, referencePath.indexOf('/'));
- },
- projectPath() {
- const { referencePath = '' } = this.activeBoardItem;
- return referencePath.slice(0, referencePath.indexOf('#'));
- },
- dropdownText() {
- return this.activeBoardItem.milestone?.title ?? this.$options.i18n.noMilestone;
- },
- },
- methods: {
- ...mapActions(['setActiveIssueMilestone']),
- handleOpen() {
- this.edit = true;
- this.$refs.dropdown.show();
- },
- handleClose() {
- this.edit = false;
- this.$refs.sidebarItem.collapse();
- },
- async setMilestone(milestoneId) {
- this.loading = true;
- this.searchTitle = '';
- this.handleClose();
-
- try {
- const input = { milestoneId, projectPath: this.projectPath };
- await this.setActiveIssueMilestone(input);
- } catch (e) {
- createFlash({ message: this.$options.i18n.updateMilestoneError });
- } finally {
- this.loading = false;
- }
- },
- },
- i18n: {
- milestone: __('Milestone'),
- noMilestone: __('No milestone'),
- assignMilestone: __('Assign milestone'),
- noMilestonesFound: s__('Milestones|No milestones found'),
- fetchMilestonesError: __('There was a problem fetching milestones.'),
- updateMilestoneError: __('An error occurred while updating the milestone.'),
- },
-};
-</script>
-
-<template>
- <board-editable-item
- ref="sidebarItem"
- :title="$options.i18n.milestone"
- :loading="loading"
- data-testid="sidebar-milestones"
- @open="handleOpen"
- @close="handleClose"
- >
- <template v-if="hasMilestone" #collapsed>
- <strong class="gl-text-gray-900">{{ activeBoardItem.milestone.title }}</strong>
- </template>
- <gl-dropdown
- ref="dropdown"
- :text="dropdownText"
- :header-text="$options.i18n.assignMilestone"
- block
- @hide="handleClose"
- >
- <gl-search-box-by-type ref="search" v-model.trim="searchTitle" class="gl-m-3" />
- <gl-dropdown-item
- data-testid="no-milestone-item"
- :is-check-item="true"
- :is-checked="!activeBoardItem.milestone"
- @click="setMilestone(null)"
- >
- {{ $options.i18n.noMilestone }}
- </gl-dropdown-item>
- <gl-dropdown-divider />
- <gl-loading-icon v-if="$apollo.loading" class="gl-py-4" />
- <template v-else-if="milestones.length > 0">
- <gl-dropdown-item
- v-for="milestone in milestones"
- :key="milestone.id"
- :is-check-item="true"
- :is-checked="activeBoardItem.milestone && milestone.id === activeBoardItem.milestone.id"
- data-testid="milestone-item"
- @click="setMilestone(milestone.id)"
- >
- {{ milestone.title }}
- </gl-dropdown-item>
- </template>
- <gl-dropdown-text v-else data-testid="no-milestones-found">
- {{ $options.i18n.noMilestonesFound }}
- </gl-dropdown-text>
- </gl-dropdown>
- </board-editable-item>
-</template>
diff --git a/app/assets/javascripts/boards/components/sidebar/board_sidebar_subscription.vue b/app/assets/javascripts/boards/components/sidebar/board_sidebar_subscription.vue
index 376985f7cb6..4f5c55d0c5d 100644
--- a/app/assets/javascripts/boards/components/sidebar/board_sidebar_subscription.vue
+++ b/app/assets/javascripts/boards/components/sidebar/board_sidebar_subscription.vue
@@ -1,7 +1,6 @@
<script>
import { GlToggle } from '@gitlab/ui';
import { mapGetters, mapActions } from 'vuex';
-import createFlash from '~/flash';
import { __, s__ } from '~/locale';
export default {
@@ -39,17 +38,16 @@ export default {
},
},
methods: {
- ...mapActions(['setActiveItemSubscribed']),
+ ...mapActions(['setActiveItemSubscribed', 'setError']),
async handleToggleSubscription() {
this.loading = true;
-
try {
await this.setActiveItemSubscribed({
subscribed: !this.activeBoardItem.subscribed,
projectPath: this.projectPathForActiveIssue,
});
} catch (error) {
- createFlash({ message: this.$options.i18n.updateSubscribedErrorMessage });
+ this.setError({ error, message: this.$options.i18n.updateSubscribedErrorMessage });
} finally {
this.loading = false;
}
diff --git a/app/assets/javascripts/boards/components/sidebar/board_sidebar_time_tracker.vue b/app/assets/javascripts/boards/components/sidebar/board_sidebar_time_tracker.vue
index 96d444980a8..5d61f7b2887 100644
--- a/app/assets/javascripts/boards/components/sidebar/board_sidebar_time_tracker.vue
+++ b/app/assets/javascripts/boards/components/sidebar/board_sidebar_time_tracker.vue
@@ -9,17 +9,29 @@ export default {
inject: ['timeTrackingLimitToHours'],
computed: {
...mapGetters(['activeBoardItem']),
+ initialTimeTracking() {
+ const {
+ timeEstimate,
+ totalTimeSpent,
+ humanTimeEstimate,
+ humanTotalTimeSpent,
+ } = this.activeBoardItem;
+ return {
+ timeEstimate,
+ totalTimeSpent,
+ humanTimeEstimate,
+ humanTotalTimeSpent,
+ };
+ },
},
};
</script>
<template>
<issuable-time-tracker
- :time-estimate="activeBoardItem.timeEstimate"
- :time-spent="activeBoardItem.totalTimeSpent"
- :human-time-estimate="activeBoardItem.humanTimeEstimate"
- :human-time-spent="activeBoardItem.humanTotalTimeSpent"
+ :issuable-iid="activeBoardItem.iid.toString()"
:limit-to-hours="timeTrackingLimitToHours"
+ :initial-time-tracking="initialTimeTracking"
:show-collapsed="false"
/>
</template>
diff --git a/app/assets/javascripts/boards/components/sidebar/board_sidebar_title.vue b/app/assets/javascripts/boards/components/sidebar/board_sidebar_title.vue
index b8d3107c377..e77aadfa50e 100644
--- a/app/assets/javascripts/boards/components/sidebar/board_sidebar_title.vue
+++ b/app/assets/javascripts/boards/components/sidebar/board_sidebar_title.vue
@@ -2,7 +2,6 @@
import { GlAlert, GlButton, GlForm, GlFormGroup, GlFormInput } from '@gitlab/ui';
import { mapGetters, mapActions } from 'vuex';
import BoardEditableItem from '~/boards/components/sidebar/board_editable_item.vue';
-import createFlash from '~/flash';
import { joinPaths } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import autofocusonshow from '~/vue_shared/directives/autofocusonshow';
@@ -53,7 +52,7 @@ export default {
},
},
methods: {
- ...mapActions(['setActiveItemTitle']),
+ ...mapActions(['setActiveItemTitle', 'setError']),
getPendingChangesKey(item) {
if (!item) {
return '';
@@ -97,7 +96,7 @@ export default {
this.showChangesAlert = false;
} catch (e) {
this.title = this.item.title;
- createFlash({ message: this.$options.i18n.updateTitleError });
+ this.setError({ error: e, message: this.$options.i18n.updateTitleError });
} finally {
this.loading = false;
}