Commit f4185043 authored by Olena Horal-Koretska's avatar Olena Horal-Koretska

Show alert on related issue close

parent 146eef9f
......@@ -2,7 +2,15 @@
import { mapActions, mapGetters, mapState } from 'vuex';
import dateFormat from 'dateformat';
import createFlash from '~/flash';
import { GlButton, GlFormInput, GlLink, GlLoadingIcon, GlBadge } from '@gitlab/ui';
import {
GlButton,
GlFormInput,
GlLink,
GlLoadingIcon,
GlBadge,
GlAlert,
GlSprintf,
} from '@gitlab/ui';
import { __, sprintf, n__ } from '~/locale';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import Icon from '~/vue_shared/components/icon.vue';
......@@ -26,6 +34,8 @@ export default {
Icon,
Stacktrace,
GlBadge,
GlAlert,
GlSprintf,
},
directives: {
TrackEvent: TrackEventDirective,
......@@ -85,6 +95,8 @@ export default {
return {
GQLerror: null,
issueCreationInProgress: false,
isAlertVisible: false,
closedIssueId: null,
};
},
computed: {
......@@ -184,7 +196,14 @@ export default {
onResolveStatusUpdate() {
const status =
this.errorStatus === errorStatus.RESOLVED ? errorStatus.UNRESOLVED : errorStatus.RESOLVED;
this.updateResolveStatus({ endpoint: this.issueUpdatePath, status });
// eslint-disable-next-line promise/catch-or-return
this.updateResolveStatus({ endpoint: this.issueUpdatePath, status }).then(res => {
this.closedIssueId = res.closed_issue_iid;
if (this.closedIssueId) {
this.isAlertVisible = true;
}
});
},
formatDate(date) {
return `${this.timeFormatted(date)} (${dateFormat(date, 'UTC:yyyy-mm-dd h:MM:ssTT Z')})`;
......@@ -199,6 +218,18 @@ export default {
<gl-loading-icon :size="3" />
</div>
<div v-else-if="showDetails" class="error-details">
<gl-alert v-if="isAlertVisible" @dismiss="isAlertVisible = false">
<gl-sprintf
:message="
__('The associated issue #%{issueId} has been closed as the error is now resolved.')
"
>
<template #issueId>
<span>{{ closedIssueId }}</span>
</template>
</gl-sprintf>
</gl-alert>
<div class="top-area align-items-center justify-content-between py-3">
<span v-if="!loadingStacktrace && stacktrace" v-html="reported"></span>
<div class="d-inline-flex">
......
......@@ -11,9 +11,11 @@ export const setStatus = ({ commit }, status) => {
export const updateStatus = ({ commit }, { endpoint, redirectUrl, status }) =>
service
.updateErrorStatus(endpoint, status)
.then(() => {
if (redirectUrl) visitUrl(redirectUrl);
.then(resp => {
commit(types.SET_ERROR_STATUS, status);
if (redirectUrl) visitUrl(redirectUrl);
return resp.data.result;
})
.catch(() => createFlash(__('Failed to update issue status')));
......
---
title: Close related GitLab issue on Sentry error resolve
merge_request: 23610
author:
type: added
......@@ -18666,6 +18666,9 @@ msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
msgstr ""
msgid "The associated issue #%{issueId} has been closed as the error is now resolved."
msgstr ""
msgid "The branch for this project has no active pipeline configuration."
msgstr ""
......
import { createLocalVue, shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
import { __ } from '~/locale';
import { GlLoadingIcon, GlLink, GlBadge, GlFormInput } from '@gitlab/ui';
import { GlLoadingIcon, GlLink, GlBadge, GlFormInput, GlAlert, GlSprintf } from '@gitlab/ui';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import Stacktrace from '~/error_tracking/components/stacktrace.vue';
import ErrorDetails from '~/error_tracking/components/error_details.vue';
......@@ -28,7 +28,7 @@ describe('ErrorDetails', () => {
function mountComponent() {
wrapper = shallowMount(ErrorDetails, {
stubs: { LoadingButton },
stubs: { LoadingButton, GlSprintf },
localVue,
store,
mocks,
......@@ -62,7 +62,7 @@ describe('ErrorDetails', () => {
startPollingDetails: () => {},
startPollingStacktrace: () => {},
updateIgnoreStatus: jest.fn(),
updateResolveStatus: jest.fn(),
updateResolveStatus: jest.fn().mockResolvedValue({ closed_issue_iid: 1 }),
};
getters = {
......@@ -313,6 +313,20 @@ describe('ErrorDetails', () => {
expect.objectContaining({ status: errorStatus.UNRESOLVED }),
);
});
it('should show alert with closed issueId', () => {
const findAlert = () => wrapper.find(GlAlert);
const closedIssueId = 123;
wrapper.setData({
isAlertVisible: true,
closedIssueId,
});
return wrapper.vm.$nextTick().then(() => {
expect(findAlert().exists()).toBe(true);
expect(findAlert().text()).toContain(`#${closedIssueId}`);
});
});
});
});
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment