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/sidebar')
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/assignee_title.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue6
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue22
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/user_name_with_status.vue23
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_widget.vue6
-rw-r--r--app/assets/javascripts/sidebar/components/date/sidebar_date_widget.vue22
-rw-r--r--app/assets/javascripts/sidebar/components/lock/edit_form_buttons.vue6
-rw-r--r--app/assets/javascripts/sidebar/components/participants/participants.vue4
-rw-r--r--app/assets/javascripts/sidebar/components/reviewers/reviewer_title.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/reviewers/sidebar_reviewers.vue6
-rw-r--r--app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue44
-rw-r--r--app/assets/javascripts/sidebar/components/sidebar_editable_item.vue8
-rw-r--r--app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue11
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/report.vue4
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/todo_toggle/sidebar_todo_widget.vue195
-rw-r--r--app/assets/javascripts/sidebar/components/todo_toggle/todo.vue2
-rw-r--r--app/assets/javascripts/sidebar/constants.js50
-rw-r--r--app/assets/javascripts/sidebar/lib/sidebar_move_issue.js9
-rw-r--r--app/assets/javascripts/sidebar/mount_sidebar.js78
-rw-r--r--app/assets/javascripts/sidebar/queries/epic_reference.query.graphql10
-rw-r--r--app/assets/javascripts/sidebar/queries/epic_todo.query.graphql14
-rw-r--r--app/assets/javascripts/sidebar/queries/issue_todo.query.graphql14
-rw-r--r--app/assets/javascripts/sidebar/queries/merge_request_milestone.query.graphql14
-rw-r--r--app/assets/javascripts/sidebar/queries/merge_request_todo.query.graphql14
-rw-r--r--app/assets/javascripts/sidebar/queries/milestone.fragment.graphql1
-rw-r--r--app/assets/javascripts/sidebar/queries/project_issue_milestone.mutation.graphql1
-rw-r--r--app/assets/javascripts/sidebar/queries/project_milestones.query.graphql8
-rw-r--r--app/assets/javascripts/sidebar/queries/todo_create.mutation.graphql9
-rw-r--r--app/assets/javascripts/sidebar/queries/todo_mark_done.mutation.graphql9
-rw-r--r--app/assets/javascripts/sidebar/queries/updateStatus.mutation.graphql3
-rw-r--r--app/assets/javascripts/sidebar/queries/update_merge_request_milestone.mutation.graphql17
-rw-r--r--app/assets/javascripts/sidebar/services/sidebar_service.js3
-rw-r--r--app/assets/javascripts/sidebar/sidebar_mediator.js8
35 files changed, 573 insertions, 56 deletions
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue b/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue
index adb573db652..4b3b22f6db3 100644
--- a/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue
@@ -47,7 +47,7 @@ export default {
<template>
<div class="hide-collapsed gl-line-height-20 gl-mb-2 gl-text-gray-900">
{{ assigneeTitle }}
- <gl-loading-icon v-if="loading" inline class="align-bottom" />
+ <gl-loading-icon v-if="loading" size="sm" inline class="align-bottom" />
<a
v-if="editable"
class="js-sidebar-dropdown-toggle edit-link float-right"
diff --git a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue
index 9840aa4ed66..c6877226b7d 100644
--- a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue
@@ -1,6 +1,6 @@
<script>
import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests';
-import { deprecatedCreateFlash as Flash } from '~/flash';
+import createFlash from '~/flash';
import { __ } from '~/locale';
import eventHub from '~/sidebar/event_hub';
import Store from '~/sidebar/stores/sidebar_store';
@@ -113,7 +113,9 @@ export default {
})
.catch(() => {
this.loading = false;
- return new Flash(__('Error occurred when saving assignees'));
+ return createFlash({
+ message: __('Error occurred when saving assignees'),
+ });
});
},
exposeAvailabilityStatus(users) {
diff --git a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue
index d9a974202a3..1dd05d3886e 100644
--- a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue
@@ -3,6 +3,7 @@ import { GlDropdownItem } from '@gitlab/ui';
import { cloneDeep } from 'lodash';
import Vue from 'vue';
import createFlash from '~/flash';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { IssuableType } from '~/issue_show/constants';
import { __, n__ } from '~/locale';
import SidebarAssigneesRealtime from '~/sidebar/components/assignees/assignees_realtime.vue';
@@ -80,6 +81,8 @@ export default {
selected: [],
isSettingAssignees: false,
isDirty: false,
+ oldIid: null,
+ oldSelected: null,
};
},
apollo: {
@@ -142,6 +145,14 @@ export default {
return this.currentUser.username !== undefined;
},
},
+ watch: {
+ iid(_, oldIid) {
+ if (this.isDirty) {
+ this.oldIid = oldIid;
+ this.oldSelected = this.selected;
+ }
+ },
+ },
created() {
assigneesWidget.updateAssignees = this.updateAssignees;
},
@@ -157,10 +168,14 @@ export default {
variables: {
...this.queryVariables,
assigneeUsernames,
+ iid: this.oldIid || this.iid,
},
})
.then(({ data }) => {
- this.$emit('assignees-updated', data.issuableSetAssignees.issuable.assignees.nodes);
+ this.$emit('assignees-updated', {
+ id: getIdFromGraphQLId(data.issuableSetAssignees.issuable.id),
+ assignees: data.issuableSetAssignees.issuable.assignees.nodes,
+ });
return data;
})
.catch(() => {
@@ -176,7 +191,10 @@ export default {
saveAssignees() {
if (this.isDirty) {
this.isDirty = false;
- this.updateAssignees(this.selected.map(({ username }) => username));
+ const usernames = this.oldSelected || this.selected;
+ this.updateAssignees(usernames.map(({ username }) => username));
+ this.oldIid = null;
+ this.oldSelected = null;
}
this.$el.dispatchEvent(hideDropdownEvent);
},
diff --git a/app/assets/javascripts/sidebar/components/assignees/user_name_with_status.vue b/app/assets/javascripts/sidebar/components/assignees/user_name_with_status.vue
index 41b3b6c9a45..bed84dc5706 100644
--- a/app/assets/javascripts/sidebar/components/assignees/user_name_with_status.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/user_name_with_status.vue
@@ -22,8 +22,16 @@ export default {
required: false,
default: '',
},
+ pronouns: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
computed: {
+ hasPronouns() {
+ return this.pronouns !== null && this.pronouns.trim() !== '';
+ },
isBusy() {
return isUserBusy(this.availability);
},
@@ -32,9 +40,18 @@ export default {
</script>
<template>
<span :class="containerClasses">
- <gl-sprintf v-if="isBusy" :message="s__('UserAvailability|%{author} (Busy)')">
- <template #author>{{ name }}</template>
+ <gl-sprintf :message="s__('UserAvailability|%{author} %{spanStart}(Busy)%{spanEnd}')">
+ <template #author
+ >{{ name }}
+ <span v-if="hasPronouns" class="gl-text-gray-500 gl-font-sm gl-font-weight-normal"
+ >({{ pronouns }})</span
+ ></template
+ >
+ <template #span="{ content }"
+ ><span v-if="isBusy" class="gl-text-gray-500 gl-font-sm gl-font-weight-normal">{{
+ content
+ }}</span>
+ </template>
</gl-sprintf>
- <template v-else>{{ name }}</template>
</span>
</template>
diff --git a/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_widget.vue b/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_widget.vue
index 372368707af..dc0f2b54a7b 100644
--- a/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_widget.vue
+++ b/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_widget.vue
@@ -4,7 +4,7 @@ import Vue from 'vue';
import createFlash from '~/flash';
import { __, sprintf } from '~/locale';
import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
-import { confidentialityQueries } from '~/sidebar/constants';
+import { confidentialityQueries, Tracking } from '~/sidebar/constants';
import SidebarConfidentialityContent from './sidebar_confidentiality_content.vue';
import SidebarConfidentialityForm from './sidebar_confidentiality_form.vue';
@@ -18,8 +18,8 @@ const hideDropdownEvent = new CustomEvent('hiddenGlDropdown', {
export default {
tracking: {
- event: 'click_edit_button',
- label: 'right_sidebar',
+ event: Tracking.editEvent,
+ label: Tracking.rightSidebarLabel,
property: 'confidentiality',
},
components: {
diff --git a/app/assets/javascripts/sidebar/components/date/sidebar_date_widget.vue b/app/assets/javascripts/sidebar/components/date/sidebar_date_widget.vue
index c3dfa5f8b14..1ff24dec884 100644
--- a/app/assets/javascripts/sidebar/components/date/sidebar_date_widget.vue
+++ b/app/assets/javascripts/sidebar/components/date/sidebar_date_widget.vue
@@ -5,7 +5,13 @@ import { IssuableType } from '~/issue_show/constants';
import { dateInWords, formatDate, parsePikadayDate } from '~/lib/utils/datetime_utility';
import { __, sprintf } from '~/locale';
import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
-import { dateFields, dateTypes, dueDateQueries, startDateQueries } from '~/sidebar/constants';
+import {
+ dateFields,
+ dateTypes,
+ dueDateQueries,
+ startDateQueries,
+ Tracking,
+} from '~/sidebar/constants';
import SidebarFormattedDate from './sidebar_formatted_date.vue';
import SidebarInheritDate from './sidebar_inherit_date.vue';
@@ -15,8 +21,8 @@ const hideDropdownEvent = new CustomEvent('hiddenGlDropdown', {
export default {
tracking: {
- event: 'click_edit_button',
- label: 'right_sidebar',
+ event: Tracking.editEvent,
+ label: Tracking.rightSidebarLabel,
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -149,6 +155,9 @@ export default {
},
},
methods: {
+ epicDatePopoverEl() {
+ return this.$refs?.epicDatePopover?.$el;
+ },
closeForm() {
this.$refs.editable.collapse();
this.$el.dispatchEvent(hideDropdownEvent);
@@ -249,12 +258,7 @@ export default {
:aria-label="$options.i18n.help"
data-testid="inherit-date-popover"
/>
- <gl-popover
- :target="() => $refs.epicDatePopover.$el"
- triggers="focus"
- placement="left"
- boundary="viewport"
- >
+ <gl-popover :target="epicDatePopoverEl" triggers="focus" placement="left" boundary="viewport">
<p>{{ $options.i18n.dateHelpValidMessage }}</p>
<gl-link :href="$options.dateHelpUrl" target="_blank">{{
$options.i18n.learnMore
diff --git a/app/assets/javascripts/sidebar/components/lock/edit_form_buttons.vue b/app/assets/javascripts/sidebar/components/lock/edit_form_buttons.vue
index c3f31a3d220..42d2e456a07 100644
--- a/app/assets/javascripts/sidebar/components/lock/edit_form_buttons.vue
+++ b/app/assets/javascripts/sidebar/components/lock/edit_form_buttons.vue
@@ -2,7 +2,7 @@
import { GlButton } from '@gitlab/ui';
import $ from 'jquery';
import { mapActions } from 'vuex';
-import { deprecatedCreateFlash as Flash } from '~/flash';
+import createFlash from '~/flash';
import { __, sprintf } from '../../../locale';
import eventHub from '../../event_hub';
@@ -52,7 +52,9 @@ export default {
const flashMessage = __(
'Something went wrong trying to change the locked state of this %{issuableDisplayName}',
);
- Flash(sprintf(flashMessage, { issuableDisplayName: this.issuableDisplayName }));
+ createFlash({
+ message: sprintf(flashMessage, { issuableDisplayName: this.issuableDisplayName }),
+ });
})
.finally(() => {
this.closeForm();
diff --git a/app/assets/javascripts/sidebar/components/participants/participants.vue b/app/assets/javascripts/sidebar/components/participants/participants.vue
index e85e416881c..650aa603f18 100644
--- a/app/assets/javascripts/sidebar/components/participants/participants.vue
+++ b/app/assets/javascripts/sidebar/components/participants/participants.vue
@@ -92,11 +92,11 @@ export default {
@click="onClickCollapsedIcon"
>
<gl-icon name="users" />
- <gl-loading-icon v-if="loading" />
+ <gl-loading-icon v-if="loading" size="sm" />
<span v-else data-testid="collapsed-count"> {{ participantCount }} </span>
</div>
<div v-if="showParticipantLabel" class="title hide-collapsed gl-mb-2">
- <gl-loading-icon v-if="loading" :inline="true" />
+ <gl-loading-icon v-if="loading" size="sm" :inline="true" />
{{ participantLabel }}
</div>
<div class="participants-list hide-collapsed">
diff --git a/app/assets/javascripts/sidebar/components/reviewers/reviewer_title.vue b/app/assets/javascripts/sidebar/components/reviewers/reviewer_title.vue
index 88c0b18ccc7..295027186cc 100644
--- a/app/assets/javascripts/sidebar/components/reviewers/reviewer_title.vue
+++ b/app/assets/javascripts/sidebar/components/reviewers/reviewer_title.vue
@@ -35,7 +35,7 @@ export default {
<template>
<div class="hide-collapsed gl-line-height-20 gl-mb-2 gl-text-gray-900">
{{ reviewerTitle }}
- <gl-loading-icon v-if="loading" inline class="align-bottom" />
+ <gl-loading-icon v-if="loading" size="sm" inline class="align-bottom" />
<a
v-if="editable"
class="js-sidebar-dropdown-toggle edit-link float-right"
diff --git a/app/assets/javascripts/sidebar/components/reviewers/sidebar_reviewers.vue b/app/assets/javascripts/sidebar/components/reviewers/sidebar_reviewers.vue
index c0bd54c60da..e414aaf719b 100644
--- a/app/assets/javascripts/sidebar/components/reviewers/sidebar_reviewers.vue
+++ b/app/assets/javascripts/sidebar/components/reviewers/sidebar_reviewers.vue
@@ -2,7 +2,7 @@
// NOTE! For the first iteration, we are simply copying the implementation of Assignees
// It will soon be overhauled in Issue https://gitlab.com/gitlab-org/gitlab/-/issues/233736
import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests';
-import { deprecatedCreateFlash as Flash } from '~/flash';
+import createFlash from '~/flash';
import { __ } from '~/locale';
import eventHub from '~/sidebar/event_hub';
import Store from '~/sidebar/stores/sidebar_store';
@@ -80,7 +80,9 @@ export default {
})
.catch(() => {
this.loading = false;
- return new Flash(__('Error occurred when saving reviewers'));
+ return createFlash({
+ message: __('Error occurred when saving reviewers'),
+ });
});
},
requestReview(data) {
diff --git a/app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue b/app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue
index 592cfea5e32..fdf63c23552 100644
--- a/app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue
+++ b/app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue
@@ -181,7 +181,7 @@ export default {
</gl-dropdown-item>
</gl-dropdown>
- <gl-loading-icon v-if="isUpdating" :inline="true" />
+ <gl-loading-icon v-if="isUpdating" size="sm" :inline="true" />
<severity-token v-else-if="!isDropdownShowing" :severity="selectedItem" />
</div>
diff --git a/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue b/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue
index c80ccc928b3..2e00a23de7c 100644
--- a/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue
+++ b/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue
@@ -16,11 +16,13 @@ import { IssuableType } from '~/issue_show/constants';
import { __, s__, sprintf } from '~/locale';
import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
import {
+ Tracking,
IssuableAttributeState,
IssuableAttributeType,
issuableAttributesQueries,
noAttributeId,
-} from '../constants';
+ defaultEpicSort,
+} from '~/sidebar/constants';
export default {
noAttributeId,
@@ -28,6 +30,7 @@ export default {
issuableAttributesQueries,
i18n: {
[IssuableAttributeType.Milestone]: __('Milestone'),
+ expired: __('(expired)'),
none: __('None'),
},
directives: {
@@ -73,9 +76,14 @@ export default {
type: String,
required: true,
validator(value) {
- return value === IssuableType.Issue;
+ return [IssuableType.Issue, IssuableType.MergeRequest].includes(value);
},
},
+ icon: {
+ type: String,
+ required: false,
+ default: undefined,
+ },
},
apollo: {
currentAttribute: {
@@ -117,7 +125,9 @@ export default {
return {
fullPath: this.attrWorkspacePath,
title: this.searchTerm,
+ in: this.searchTerm && this.issuableAttribute === IssuableType.Epic ? 'TITLE' : undefined,
state: this.$options.IssuableAttributeState[this.issuableAttribute],
+ sort: this.issuableAttribute === IssuableType.Epic ? defaultEpicSort : null,
};
},
update(data) {
@@ -140,8 +150,8 @@ export default {
currentAttribute: null,
attributesList: [],
tracking: {
- label: 'right_sidebar',
- event: 'click_edit_button',
+ event: Tracking.editEvent,
+ label: Tracking.rightSidebarLabel,
property: this.issuableAttribute,
},
};
@@ -170,6 +180,9 @@ export default {
attributeTypeTitle() {
return this.$options.i18n[this.issuableAttribute];
},
+ attributeTypeIcon() {
+ return this.icon || this.issuableAttribute;
+ },
i18n() {
return {
noAttribute: sprintf(s__('DropdownWidget|No %{issuableAttribute}'), {
@@ -222,7 +235,8 @@ export default {
variables: {
fullPath: this.workspacePath,
attributeId:
- this.issuableAttribute === IssuableAttributeType.Milestone
+ this.issuableAttribute === IssuableAttributeType.Milestone &&
+ this.issuableType === IssuableType.Issue
? getIdFromGraphQLId(attributeId)
: attributeId,
iid: this.iid,
@@ -253,6 +267,11 @@ export default {
attributeId === this.currentAttribute?.id || (!this.currentAttribute?.id && !attributeId)
);
},
+ isAttributeOverdue(attribute) {
+ return this.issuableAttribute === IssuableAttributeType.Milestone
+ ? attribute?.expired
+ : false;
+ },
showDropdown() {
this.$refs.newDropdown.show();
},
@@ -282,8 +301,10 @@ export default {
>
<template #collapsed>
<div v-if="isClassicSidebar" v-gl-tooltip class="sidebar-collapsed-icon">
- <gl-icon :size="16" :aria-label="attributeTypeTitle" :name="issuableAttribute" />
- <span class="collapse-truncated-title">{{ attributeTitle }}</span>
+ <gl-icon :size="16" :aria-label="attributeTypeTitle" :name="attributeTypeIcon" />
+ <span class="collapse-truncated-title">
+ {{ attributeTitle }}
+ </span>
</div>
<div
:data-testid="`select-${issuableAttribute}`"
@@ -300,8 +321,13 @@ export default {
:attributeUrl="attributeUrl"
:currentAttribute="currentAttribute"
>
- <gl-link class="gl-text-gray-900! gl-font-weight-bold" :href="attributeUrl">
+ <gl-link
+ class="gl-text-gray-900! gl-font-weight-bold"
+ :href="attributeUrl"
+ :data-qa-selector="`${issuableAttribute}_link`"
+ >
{{ attributeTitle }}
+ <span v-if="isAttributeOverdue(currentAttribute)">{{ $options.i18n.expired }}</span>
</gl-link>
</slot>
</div>
@@ -328,6 +354,7 @@ export default {
<gl-dropdown-divider />
<gl-loading-icon
v-if="$apollo.queries.attributesList.loading"
+ size="sm"
class="gl-py-4"
data-testid="loading-icon-dropdown"
/>
@@ -351,6 +378,7 @@ export default {
@click="updateAttribute(attrItem.id)"
>
{{ attrItem.title }}
+ <span v-if="isAttributeOverdue(attrItem)">{{ $options.i18n.expired }}</span>
</gl-dropdown-item>
</slot>
</template>
diff --git a/app/assets/javascripts/sidebar/components/sidebar_editable_item.vue b/app/assets/javascripts/sidebar/components/sidebar_editable_item.vue
index 825d7ff5841..7c496cc422a 100644
--- a/app/assets/javascripts/sidebar/components/sidebar_editable_item.vue
+++ b/app/assets/javascripts/sidebar/components/sidebar_editable_item.vue
@@ -117,9 +117,15 @@ export default {
{{ title }}
</span>
<slot name="title-extra"></slot>
- <gl-loading-icon v-if="loading || initialLoading" inline class="gl-ml-2 hide-collapsed" />
+ <gl-loading-icon
+ v-if="loading || initialLoading"
+ size="sm"
+ inline
+ class="gl-ml-2 hide-collapsed"
+ />
<gl-loading-icon
v-if="loading && isClassicSidebar"
+ size="sm"
inline
class="gl-mx-auto gl-my-0 hide-expanded"
/>
diff --git a/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue b/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue
index e97742a1339..bc7e377a966 100644
--- a/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue
+++ b/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue
@@ -2,17 +2,18 @@
import { GlIcon, GlLoadingIcon, GlToggle, GlTooltipDirective } from '@gitlab/ui';
import createFlash from '~/flash';
import { IssuableType } from '~/issue_show/constants';
+import { isLoggedIn } from '~/lib/utils/common_utils';
import { __, sprintf } from '~/locale';
import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
-import { subscribedQueries } from '~/sidebar/constants';
+import { subscribedQueries, Tracking } from '~/sidebar/constants';
const ICON_ON = 'notifications';
const ICON_OFF = 'notifications-off';
export default {
tracking: {
- event: 'click_edit_button',
- label: 'right_sidebar',
+ event: Tracking.editEvent,
+ label: Tracking.rightSidebarLabel,
property: 'subscriptions',
},
directives: {
@@ -102,7 +103,7 @@ export default {
});
},
isLoggedIn() {
- return Boolean(gon.current_user_id);
+ return isLoggedIn();
},
canSubscribe() {
return this.emailsDisabled || !this.isLoggedIn;
@@ -195,7 +196,7 @@ export default {
class="sidebar-collapsed-icon"
@click="toggleSubscribed"
>
- <gl-loading-icon v-if="isLoading" class="sidebar-item-icon is-active" />
+ <gl-loading-icon v-if="isLoading" size="sm" class="sidebar-item-icon is-active" />
<gl-icon v-else :name="notificationIcon" :size="16" class="sidebar-item-icon is-active" />
</span>
<div v-show="emailsDisabled" class="gl-mt-3 hide-collapsed gl-text-gray-500">
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/report.vue b/app/assets/javascripts/sidebar/components/time_tracking/report.vue
index f91a78b7f1d..8a14998910b 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/report.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/report.vue
@@ -1,6 +1,7 @@
<script>
import { GlLoadingIcon, GlTable } from '@gitlab/ui';
import createFlash from '~/flash';
+import { TYPE_ISSUE, TYPE_MERGE_REQUEST } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import { formatDate, parseSeconds, stringifyTime } from '~/lib/utils/datetime_utility';
import { __ } from '~/locale';
@@ -52,8 +53,7 @@ export default {
return this.issuableType === 'issue';
},
getGraphQLEntityType() {
- // eslint-disable-next-line @gitlab/require-i18n-strings
- return this.isIssue() ? 'Issue' : 'MergeRequest';
+ return this.isIssue() ? TYPE_ISSUE : TYPE_MERGE_REQUEST;
},
extractTimelogs(data) {
const timelogs = data?.issuable?.timelogs?.nodes || [];
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
index 87ddbbf256a..9a9d03353dc 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
@@ -200,7 +200,7 @@ export default {
/>
<div class="hide-collapsed gl-line-height-20 gl-text-gray-900">
{{ __('Time tracking') }}
- <gl-loading-icon v-if="isTimeTrackingInfoLoading" inline />
+ <gl-loading-icon v-if="isTimeTrackingInfoLoading" size="sm" inline />
<div
v-if="!showHelpState"
data-testid="helpButton"
diff --git a/app/assets/javascripts/sidebar/components/todo_toggle/sidebar_todo_widget.vue b/app/assets/javascripts/sidebar/components/todo_toggle/sidebar_todo_widget.vue
new file mode 100644
index 00000000000..a9c4203af22
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/todo_toggle/sidebar_todo_widget.vue
@@ -0,0 +1,195 @@
+<script>
+import { GlButton, GlIcon, GlTooltipDirective } from '@gitlab/ui';
+import { produce } from 'immer';
+import createFlash from '~/flash';
+import { __, sprintf } from '~/locale';
+import { todoQueries, TodoMutationTypes, todoMutations } from '~/sidebar/constants';
+import { todoLabel } from '~/vue_shared/components/sidebar/todo_toggle//utils';
+import TodoButton from '~/vue_shared/components/sidebar/todo_toggle/todo_button.vue';
+
+export default {
+ components: {
+ GlButton,
+ GlIcon,
+ TodoButton,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ inject: {
+ isClassicSidebar: {
+ default: false,
+ },
+ },
+ props: {
+ issuableId: {
+ type: String,
+ required: true,
+ },
+ issuableIid: {
+ type: String,
+ required: true,
+ },
+ fullPath: {
+ type: String,
+ required: true,
+ },
+ issuableType: {
+ required: true,
+ type: String,
+ },
+ },
+ data() {
+ return {
+ loading: false,
+ };
+ },
+ apollo: {
+ todoId: {
+ query() {
+ return todoQueries[this.issuableType].query;
+ },
+ variables() {
+ return {
+ fullPath: this.fullPath,
+ iid: String(this.issuableIid),
+ };
+ },
+ update(data) {
+ return data.workspace?.issuable?.currentUserTodos.nodes[0]?.id;
+ },
+ result({ data }) {
+ const currentUserTodos = data.workspace?.issuable?.currentUserTodos?.nodes ?? [];
+ this.todoId = currentUserTodos[0]?.id;
+ this.$emit('todoUpdated', currentUserTodos.length > 0);
+ },
+ error() {
+ createFlash({
+ message: sprintf(__('Something went wrong while setting %{issuableType} to-do item.'), {
+ issuableType: this.issuableType,
+ }),
+ });
+ },
+ },
+ },
+ computed: {
+ todoIdQuery() {
+ return todoQueries[this.issuableType].query;
+ },
+ todoIdQueryVariables() {
+ return {
+ fullPath: this.fullPath,
+ iid: String(this.issuableIid),
+ };
+ },
+ isLoading() {
+ return this.$apollo.queries?.todoId?.loading || this.loading;
+ },
+ hasTodo() {
+ return Boolean(this.todoId);
+ },
+ todoMutationType() {
+ if (this.hasTodo) {
+ return TodoMutationTypes.MarkDone;
+ }
+ return TodoMutationTypes.Create;
+ },
+ collapsedButtonIcon() {
+ return this.hasTodo ? 'todo-done' : 'todo-add';
+ },
+ tootltipTitle() {
+ return todoLabel(this.hasTodo);
+ },
+ },
+ methods: {
+ toggleTodo() {
+ this.loading = true;
+ this.$apollo
+ .mutate({
+ mutation: todoMutations[this.todoMutationType],
+ variables: {
+ input: {
+ targetId: !this.hasTodo ? this.issuableId : undefined,
+ id: this.hasTodo ? this.todoId : undefined,
+ },
+ },
+ update: (
+ store,
+ {
+ data: {
+ todoMutation: { todo },
+ },
+ },
+ ) => {
+ const queryProps = {
+ query: this.todoIdQuery,
+ variables: this.todoIdQueryVariables,
+ };
+
+ const sourceData = store.readQuery(queryProps);
+ const data = produce(sourceData, (draftState) => {
+ draftState.workspace.issuable.currentUserTodos.nodes = this.hasTodo ? [] : [todo];
+ });
+ store.writeQuery({
+ data,
+ ...queryProps,
+ });
+ },
+ })
+ .then(
+ ({
+ data: {
+ todoMutation: { errors },
+ },
+ }) => {
+ if (errors.length) {
+ createFlash({
+ message: errors[0],
+ });
+ }
+ },
+ )
+ .catch(() => {
+ createFlash({
+ message: sprintf(__('Something went wrong while setting %{issuableType} to-do item.'), {
+ issuableType: this.issuableType,
+ }),
+ });
+ })
+ .finally(() => {
+ this.loading = false;
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <div data-testid="sidebar-todo">
+ <todo-button
+ :issuable-type="issuableType"
+ :issuable-id="issuableId"
+ :is-todo="hasTodo"
+ :loading="isLoading"
+ size="small"
+ class="hide-collapsed"
+ @click.stop.prevent="toggleTodo"
+ />
+ <gl-button
+ v-if="isClassicSidebar"
+ category="tertiary"
+ type="reset"
+ class="sidebar-collapsed-icon sidebar-collapsed-container gl-rounded-0! gl-shadow-none!"
+ @click.stop.prevent="toggleTodo"
+ >
+ <gl-icon
+ v-gl-tooltip.left.viewport
+ :title="tootltipTitle"
+ :size="16"
+ :class="{ 'todo-undone': hasTodo }"
+ :name="collapsedButtonIcon"
+ :aria-label="collapsedButtonIcon"
+ />
+ </gl-button>
+ </div>
+</template>
diff --git a/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue b/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue
index f589e7555b3..f7e76cc2b7f 100644
--- a/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue
+++ b/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue
@@ -85,6 +85,6 @@ export default {
:name="collapsedButtonIcon"
/>
<span v-show="!collapsed" class="issuable-todo-inner">{{ buttonLabel }}</span>
- <gl-loading-icon v-show="isActionActive" :inline="true" />
+ <gl-loading-icon v-show="isActionActive" size="sm" :inline="true" />
</button>
</template>
diff --git a/app/assets/javascripts/sidebar/constants.js b/app/assets/javascripts/sidebar/constants.js
index e8e69c19d9f..08ee4379c0c 100644
--- a/app/assets/javascripts/sidebar/constants.js
+++ b/app/assets/javascripts/sidebar/constants.js
@@ -1,18 +1,26 @@
import { IssuableType } from '~/issue_show/constants';
+import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import epicConfidentialQuery from '~/sidebar/queries/epic_confidential.query.graphql';
import epicDueDateQuery from '~/sidebar/queries/epic_due_date.query.graphql';
import epicParticipantsQuery from '~/sidebar/queries/epic_participants.query.graphql';
+import epicReferenceQuery from '~/sidebar/queries/epic_reference.query.graphql';
import epicStartDateQuery from '~/sidebar/queries/epic_start_date.query.graphql';
import epicSubscribedQuery from '~/sidebar/queries/epic_subscribed.query.graphql';
+import epicTodoQuery from '~/sidebar/queries/epic_todo.query.graphql';
import issuableAssigneesSubscription from '~/sidebar/queries/issuable_assignees.subscription.graphql';
import issueConfidentialQuery from '~/sidebar/queries/issue_confidential.query.graphql';
import issueDueDateQuery from '~/sidebar/queries/issue_due_date.query.graphql';
import issueReferenceQuery from '~/sidebar/queries/issue_reference.query.graphql';
import issueSubscribedQuery from '~/sidebar/queries/issue_subscribed.query.graphql';
import issueTimeTrackingQuery from '~/sidebar/queries/issue_time_tracking.query.graphql';
+import issueTodoQuery from '~/sidebar/queries/issue_todo.query.graphql';
+import mergeRequestMilestone from '~/sidebar/queries/merge_request_milestone.query.graphql';
import mergeRequestReferenceQuery from '~/sidebar/queries/merge_request_reference.query.graphql';
import mergeRequestSubscribed from '~/sidebar/queries/merge_request_subscribed.query.graphql';
import mergeRequestTimeTrackingQuery from '~/sidebar/queries/merge_request_time_tracking.query.graphql';
+import mergeRequestTodoQuery from '~/sidebar/queries/merge_request_todo.query.graphql';
+import todoCreateMutation from '~/sidebar/queries/todo_create.mutation.graphql';
+import todoMarkDoneMutation from '~/sidebar/queries/todo_mark_done.mutation.graphql';
import updateEpicConfidentialMutation from '~/sidebar/queries/update_epic_confidential.mutation.graphql';
import updateEpicDueDateMutation from '~/sidebar/queries/update_epic_due_date.mutation.graphql';
import updateEpicStartDateMutation from '~/sidebar/queries/update_epic_start_date.mutation.graphql';
@@ -20,6 +28,7 @@ import updateEpicSubscriptionMutation from '~/sidebar/queries/update_epic_subscr
import updateIssueConfidentialMutation from '~/sidebar/queries/update_issue_confidential.mutation.graphql';
import updateIssueDueDateMutation from '~/sidebar/queries/update_issue_due_date.mutation.graphql';
import updateIssueSubscriptionMutation from '~/sidebar/queries/update_issue_subscription.mutation.graphql';
+import mergeRequestMilestoneMutation from '~/sidebar/queries/update_merge_request_milestone.mutation.graphql';
import updateMergeRequestSubscriptionMutation from '~/sidebar/queries/update_merge_request_subscription.mutation.graphql';
import updateAlertAssigneesMutation from '~/vue_shared/alert_details/graphql/mutations/alert_set_assignees.mutation.graphql';
import getAlertAssignees from '~/vue_shared/components/sidebar/queries/get_alert_assignees.query.graphql';
@@ -35,7 +44,9 @@ import projectIssueMilestoneMutation from './queries/project_issue_milestone.mut
import projectIssueMilestoneQuery from './queries/project_issue_milestone.query.graphql';
import projectMilestonesQuery from './queries/project_milestones.query.graphql';
-export const ASSIGNEES_DEBOUNCE_DELAY = 250;
+export const ASSIGNEES_DEBOUNCE_DELAY = DEFAULT_DEBOUNCE_AND_THROTTLE_MS;
+
+export const defaultEpicSort = 'TITLE_ASC';
export const assigneesQueries = {
[IssuableType.Issue]: {
@@ -87,6 +98,9 @@ export const referenceQueries = {
[IssuableType.MergeRequest]: {
query: mergeRequestReferenceQuery,
},
+ [IssuableType.Epic]: {
+ query: epicReferenceQuery,
+ },
};
export const dateTypes = {
@@ -122,6 +136,11 @@ export const subscribedQueries = {
},
};
+export const Tracking = {
+ editEvent: 'click_edit_button',
+ rightSidebarLabel: 'right_sidebar',
+};
+
export const timeTrackingQueries = {
[IssuableType.Issue]: {
query: issueTimeTrackingQuery,
@@ -165,12 +184,19 @@ export const issuableMilestoneQueries = {
query: projectIssueMilestoneQuery,
mutation: projectIssueMilestoneMutation,
},
+ [IssuableType.MergeRequest]: {
+ query: mergeRequestMilestone,
+ mutation: mergeRequestMilestoneMutation,
+ },
};
export const milestonesQueries = {
[IssuableType.Issue]: {
query: projectMilestonesQuery,
},
+ [IssuableType.MergeRequest]: {
+ query: projectMilestonesQuery,
+ },
};
export const IssuableAttributeType = {
@@ -187,3 +213,25 @@ export const issuableAttributesQueries = {
list: milestonesQueries,
},
};
+
+export const todoQueries = {
+ [IssuableType.Epic]: {
+ query: epicTodoQuery,
+ },
+ [IssuableType.Issue]: {
+ query: issueTodoQuery,
+ },
+ [IssuableType.MergeRequest]: {
+ query: mergeRequestTodoQuery,
+ },
+};
+
+export const TodoMutationTypes = {
+ Create: 'create',
+ MarkDone: 'mark-done',
+};
+
+export const todoMutations = {
+ [TodoMutationTypes.Create]: todoCreateMutation,
+ [TodoMutationTypes.MarkDone]: todoMarkDoneMutation,
+};
diff --git a/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js b/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js
index 21cd24b0842..5a3122e83d0 100644
--- a/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js
+++ b/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js
@@ -1,6 +1,7 @@
import $ from 'jquery';
import { escape } from 'lodash';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
+import createFlash from '~/flash';
import { __ } from '~/locale';
function isValidProjectId(id) {
@@ -42,8 +43,10 @@ class SidebarMoveIssue {
this.mediator
.fetchAutocompleteProjects(searchTerm)
.then(callback)
- .catch(
- () => new window.Flash(__('An error occurred while fetching projects autocomplete.')),
+ .catch(() =>
+ createFlash({
+ message: __('An error occurred while fetching projects autocomplete.'),
+ }),
);
},
renderRow: (project) => `
@@ -76,7 +79,7 @@ class SidebarMoveIssue {
this.$confirmButton.disable().addClass('is-loading');
this.mediator.moveIssue().catch(() => {
- window.Flash(__('An error occurred while moving the issue.'));
+ createFlash({ message: __('An error occurred while moving the issue.') });
this.$confirmButton.enable().removeClass('is-loading');
});
}
diff --git a/app/assets/javascripts/sidebar/mount_sidebar.js b/app/assets/javascripts/sidebar/mount_sidebar.js
index 67c72b17f1f..dd1b439c482 100644
--- a/app/assets/javascripts/sidebar/mount_sidebar.js
+++ b/app/assets/javascripts/sidebar/mount_sidebar.js
@@ -2,6 +2,8 @@ import $ from 'jquery';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createFlash from '~/flash';
+import { TYPE_ISSUE, TYPE_MERGE_REQUEST } from '~/graphql_shared/constants';
+import { convertToGraphQLId } from '~/graphql_shared/utils';
import initInviteMembersModal from '~/invite_members/init_invite_members_modal';
import initInviteMembersTrigger from '~/invite_members/init_invite_members_trigger';
import { IssuableType } from '~/issue_show/constants';
@@ -18,6 +20,8 @@ import SidebarConfidentialityWidget from '~/sidebar/components/confidential/side
import SidebarDueDateWidget from '~/sidebar/components/date/sidebar_date_widget.vue';
import SidebarParticipantsWidget from '~/sidebar/components/participants/sidebar_participants_widget.vue';
import SidebarReferenceWidget from '~/sidebar/components/reference/sidebar_reference_widget.vue';
+import SidebarDropdownWidget from '~/sidebar/components/sidebar_dropdown_widget.vue';
+import SidebarTodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue';
import { apolloProvider } from '~/sidebar/graphql';
import trackShowInviteMemberLink from '~/sidebar/track_invite_members';
import Translate from '../vue_shared/translate';
@@ -29,6 +33,7 @@ import SidebarReviewers from './components/reviewers/sidebar_reviewers.vue';
import SidebarSeverity from './components/severity/sidebar_severity.vue';
import SidebarSubscriptionsWidget from './components/subscriptions/sidebar_subscriptions_widget.vue';
import SidebarTimeTracking from './components/time_tracking/sidebar_time_tracking.vue';
+import { IssuableAttributeType } from './constants';
import SidebarMoveIssue from './lib/sidebar_move_issue';
Vue.use(Translate);
@@ -38,6 +43,40 @@ function getSidebarOptions(sidebarOptEl = document.querySelector('.js-sidebar-op
return JSON.parse(sidebarOptEl.innerHTML);
}
+function mountSidebarToDoWidget() {
+ const el = document.querySelector('.js-issuable-todo');
+
+ if (!el) {
+ return false;
+ }
+
+ const { projectPath, iid, id } = el.dataset;
+
+ return new Vue({
+ el,
+ apolloProvider,
+ components: {
+ SidebarTodoWidget,
+ },
+ provide: {
+ isClassicSidebar: true,
+ },
+ render: (createElement) =>
+ createElement('sidebar-todo-widget', {
+ props: {
+ fullPath: projectPath,
+ issuableId:
+ isInIssuePage() || isInDesignPage()
+ ? convertToGraphQLId(TYPE_ISSUE, id)
+ : convertToGraphQLId(TYPE_MERGE_REQUEST, id),
+ issuableIid: iid,
+ issuableType:
+ isInIssuePage() || isInDesignPage() ? IssuableType.Issue : IssuableType.MergeRequest,
+ },
+ }),
+ });
+}
+
function getSidebarAssigneeAvailabilityData() {
const sidebarAssigneeEl = document.querySelectorAll('.js-sidebar-assignee-data input');
return Array.from(sidebarAssigneeEl)
@@ -154,7 +193,8 @@ function mountReviewersComponent(mediator) {
issuableIid: String(iid),
projectPath: fullPath,
field: el.dataset.field,
- issuableType: isInIssuePage() || isInDesignPage() ? 'issue' : 'merge_request',
+ issuableType:
+ isInIssuePage() || isInDesignPage() ? IssuableType.Issue : IssuableType.MergeRequest,
},
}),
});
@@ -166,6 +206,40 @@ function mountReviewersComponent(mediator) {
}
}
+function mountMilestoneSelect() {
+ const el = document.querySelector('.js-milestone-select');
+
+ if (!el) {
+ return false;
+ }
+
+ const { canEdit, projectPath, issueIid } = el.dataset;
+
+ return new Vue({
+ el,
+ apolloProvider,
+ components: {
+ SidebarDropdownWidget,
+ },
+ provide: {
+ canUpdate: parseBoolean(canEdit),
+ isClassicSidebar: true,
+ },
+ render: (createElement) =>
+ createElement('sidebar-dropdown-widget', {
+ props: {
+ attrWorkspacePath: projectPath,
+ workspacePath: projectPath,
+ iid: issueIid,
+ issuableType:
+ isInIssuePage() || isInDesignPage() ? IssuableType.Issue : IssuableType.MergeRequest,
+ issuableAttribute: IssuableAttributeType.Milestone,
+ icon: 'clock',
+ },
+ }),
+ });
+}
+
export function mountSidebarLabels() {
const el = document.querySelector('.js-sidebar-labels');
@@ -460,12 +534,14 @@ export function mountSidebar(mediator) {
initInviteMembersModal();
initInviteMembersTrigger();
+ mountSidebarToDoWidget();
if (isAssigneesWidgetShown) {
mountAssigneesComponent();
} else {
mountAssigneesComponentDeprecated(mediator);
}
mountReviewersComponent(mediator);
+ mountMilestoneSelect();
mountConfidentialComponent(mediator);
mountDueDateComponent(mediator);
mountReferenceComponent(mediator);
diff --git a/app/assets/javascripts/sidebar/queries/epic_reference.query.graphql b/app/assets/javascripts/sidebar/queries/epic_reference.query.graphql
new file mode 100644
index 00000000000..bd10f09aed8
--- /dev/null
+++ b/app/assets/javascripts/sidebar/queries/epic_reference.query.graphql
@@ -0,0 +1,10 @@
+query epicReference($fullPath: ID!, $iid: ID) {
+ workspace: group(fullPath: $fullPath) {
+ __typename
+ issuable: epic(iid: $iid) {
+ __typename
+ id
+ reference(full: true)
+ }
+ }
+}
diff --git a/app/assets/javascripts/sidebar/queries/epic_todo.query.graphql b/app/assets/javascripts/sidebar/queries/epic_todo.query.graphql
new file mode 100644
index 00000000000..1e6f9bad5b2
--- /dev/null
+++ b/app/assets/javascripts/sidebar/queries/epic_todo.query.graphql
@@ -0,0 +1,14 @@
+query epicTodos($fullPath: ID!, $iid: ID) {
+ workspace: group(fullPath: $fullPath) {
+ __typename
+ issuable: epic(iid: $iid) {
+ __typename
+ id
+ currentUserTodos(state: pending) {
+ nodes {
+ id
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/sidebar/queries/issue_todo.query.graphql b/app/assets/javascripts/sidebar/queries/issue_todo.query.graphql
new file mode 100644
index 00000000000..783d36352fe
--- /dev/null
+++ b/app/assets/javascripts/sidebar/queries/issue_todo.query.graphql
@@ -0,0 +1,14 @@
+query issueTodos($fullPath: ID!, $iid: String!) {
+ workspace: project(fullPath: $fullPath) {
+ __typename
+ issuable: issue(iid: $iid) {
+ __typename
+ id
+ currentUserTodos(state: pending) {
+ nodes {
+ id
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/sidebar/queries/merge_request_milestone.query.graphql b/app/assets/javascripts/sidebar/queries/merge_request_milestone.query.graphql
new file mode 100644
index 00000000000..5c0edf5acee
--- /dev/null
+++ b/app/assets/javascripts/sidebar/queries/merge_request_milestone.query.graphql
@@ -0,0 +1,14 @@
+#import "./milestone.fragment.graphql"
+
+query mergeRequestMilestone($fullPath: ID!, $iid: String!) {
+ workspace: project(fullPath: $fullPath) {
+ __typename
+ issuable: mergeRequest(iid: $iid) {
+ __typename
+ id
+ attribute: milestone {
+ ...MilestoneFragment
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/sidebar/queries/merge_request_todo.query.graphql b/app/assets/javascripts/sidebar/queries/merge_request_todo.query.graphql
new file mode 100644
index 00000000000..93a1c9ea925
--- /dev/null
+++ b/app/assets/javascripts/sidebar/queries/merge_request_todo.query.graphql
@@ -0,0 +1,14 @@
+query mergeRequestTodos($fullPath: ID!, $iid: String!) {
+ workspace: project(fullPath: $fullPath) {
+ __typename
+ issuable: mergeRequest(iid: $iid) {
+ __typename
+ id
+ currentUserTodos(state: pending) {
+ nodes {
+ id
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/sidebar/queries/milestone.fragment.graphql b/app/assets/javascripts/sidebar/queries/milestone.fragment.graphql
index 8db5359dac0..2ffd58a2da1 100644
--- a/app/assets/javascripts/sidebar/queries/milestone.fragment.graphql
+++ b/app/assets/javascripts/sidebar/queries/milestone.fragment.graphql
@@ -2,4 +2,5 @@ fragment MilestoneFragment on Milestone {
id
title
webUrl: webPath
+ expired
}
diff --git a/app/assets/javascripts/sidebar/queries/project_issue_milestone.mutation.graphql b/app/assets/javascripts/sidebar/queries/project_issue_milestone.mutation.graphql
index d88ad8b1087..721a71bef63 100644
--- a/app/assets/javascripts/sidebar/queries/project_issue_milestone.mutation.graphql
+++ b/app/assets/javascripts/sidebar/queries/project_issue_milestone.mutation.graphql
@@ -11,6 +11,7 @@ mutation projectIssueMilestoneMutation($fullPath: ID!, $iid: String!, $attribute
title
id
state
+ expired
}
}
}
diff --git a/app/assets/javascripts/sidebar/queries/project_milestones.query.graphql b/app/assets/javascripts/sidebar/queries/project_milestones.query.graphql
index 1237640c468..a3ab1ebc872 100644
--- a/app/assets/javascripts/sidebar/queries/project_milestones.query.graphql
+++ b/app/assets/javascripts/sidebar/queries/project_milestones.query.graphql
@@ -3,7 +3,13 @@
query projectMilestones($fullPath: ID!, $title: String, $state: MilestoneStateEnum) {
workspace: project(fullPath: $fullPath) {
__typename
- attributes: milestones(searchTitle: $title, state: $state) {
+ attributes: milestones(
+ searchTitle: $title
+ state: $state
+ sort: EXPIRED_LAST_DUE_DATE_ASC
+ first: 20
+ includeAncestors: true
+ ) {
nodes {
...MilestoneFragment
state
diff --git a/app/assets/javascripts/sidebar/queries/todo_create.mutation.graphql b/app/assets/javascripts/sidebar/queries/todo_create.mutation.graphql
new file mode 100644
index 00000000000..4675db9153e
--- /dev/null
+++ b/app/assets/javascripts/sidebar/queries/todo_create.mutation.graphql
@@ -0,0 +1,9 @@
+mutation issuableTodoCreate($input: TodoCreateInput!) {
+ todoMutation: todoCreate(input: $input) {
+ __typename
+ todo {
+ id
+ }
+ errors
+ }
+}
diff --git a/app/assets/javascripts/sidebar/queries/todo_mark_done.mutation.graphql b/app/assets/javascripts/sidebar/queries/todo_mark_done.mutation.graphql
new file mode 100644
index 00000000000..8253e5e82bc
--- /dev/null
+++ b/app/assets/javascripts/sidebar/queries/todo_mark_done.mutation.graphql
@@ -0,0 +1,9 @@
+mutation issuableTodoMarkDone($input: TodoMarkDoneInput!) {
+ todoMutation: todoMarkDone(input: $input) {
+ __typename
+ todo {
+ id
+ }
+ errors
+ }
+}
diff --git a/app/assets/javascripts/sidebar/queries/updateStatus.mutation.graphql b/app/assets/javascripts/sidebar/queries/updateStatus.mutation.graphql
index b45b6b46c8f..28a47735143 100644
--- a/app/assets/javascripts/sidebar/queries/updateStatus.mutation.graphql
+++ b/app/assets/javascripts/sidebar/queries/updateStatus.mutation.graphql
@@ -1,6 +1,7 @@
mutation($projectPath: ID!, $iid: String!, $healthStatus: HealthStatus) {
updateIssue(input: { projectPath: $projectPath, iid: $iid, healthStatus: $healthStatus }) {
- issue {
+ issuable: issue {
+ id
healthStatus
}
errors
diff --git a/app/assets/javascripts/sidebar/queries/update_merge_request_milestone.mutation.graphql b/app/assets/javascripts/sidebar/queries/update_merge_request_milestone.mutation.graphql
new file mode 100644
index 00000000000..368f06fac7f
--- /dev/null
+++ b/app/assets/javascripts/sidebar/queries/update_merge_request_milestone.mutation.graphql
@@ -0,0 +1,17 @@
+mutation mergeRequestSetMilestone($fullPath: ID!, $iid: String!, $attributeId: ID) {
+ issuableSetAttribute: mergeRequestSetMilestone(
+ input: { projectPath: $fullPath, iid: $iid, milestoneId: $attributeId }
+ ) {
+ __typename
+ errors
+ issuable: mergeRequest {
+ __typename
+ id
+ attribute: milestone {
+ title
+ id
+ state
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/sidebar/services/sidebar_service.js b/app/assets/javascripts/sidebar/services/sidebar_service.js
index 88501f2c305..ace2a163adc 100644
--- a/app/assets/javascripts/sidebar/services/sidebar_service.js
+++ b/app/assets/javascripts/sidebar/services/sidebar_service.js
@@ -1,4 +1,5 @@
import sidebarDetailsIssueQuery from 'ee_else_ce/sidebar/queries/sidebarDetails.query.graphql';
+import { TYPE_USER } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import createGqClient, { fetchPolicies } from '~/lib/graphql';
import axios from '~/lib/utils/axios_utils';
@@ -88,7 +89,7 @@ export default class SidebarService {
return gqClient.mutate({
mutation: reviewerRereviewMutation,
variables: {
- userId: convertToGraphQLId('User', `${userId}`), // eslint-disable-line @gitlab/require-i18n-strings
+ userId: convertToGraphQLId(TYPE_USER, `${userId}`),
projectPath: this.fullPath,
iid: this.iid.toString(),
},
diff --git a/app/assets/javascripts/sidebar/sidebar_mediator.js b/app/assets/javascripts/sidebar/sidebar_mediator.js
index 3595354da80..0a5e44a9b95 100644
--- a/app/assets/javascripts/sidebar/sidebar_mediator.js
+++ b/app/assets/javascripts/sidebar/sidebar_mediator.js
@@ -1,7 +1,7 @@
import Store from 'ee_else_ce/sidebar/stores/sidebar_store';
+import createFlash from '~/flash';
import { __ } from '~/locale';
import toast from '~/vue_shared/plugins/global_toast';
-import { deprecatedCreateFlash as Flash } from '../flash';
import { visitUrl } from '../lib/utils/url_utility';
import Service from './services/sidebar_service';
@@ -74,7 +74,11 @@ export default class SidebarMediator {
.then(([restResponse, graphQlResponse]) => {
this.processFetchedData(restResponse.data, graphQlResponse.data);
})
- .catch(() => new Flash(__('Error occurred when fetching sidebar data')));
+ .catch(() =>
+ createFlash({
+ message: __('Error occurred when fetching sidebar data'),
+ }),
+ );
}
processFetchedData(data) {