diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-08-20 21:42:06 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-08-20 21:42:06 +0300 |
commit | 6e4e1050d9dba2b7b2523fdd1768823ab85feef4 (patch) | |
tree | 78be5963ec075d80116a932011d695dd33910b4e /app/assets/javascripts/alert_management | |
parent | 1ce776de4ae122aba3f349c02c17cebeaa8ecf07 (diff) |
Add latest changes from gitlab-org/gitlab@13-3-stable-ee
Diffstat (limited to 'app/assets/javascripts/alert_management')
17 files changed, 233 insertions, 149 deletions
diff --git a/app/assets/javascripts/alert_management/components/alert_details.vue b/app/assets/javascripts/alert_management/components/alert_details.vue index 0731349630c..5d260fcc200 100644 --- a/app/assets/javascripts/alert_management/components/alert_details.vue +++ b/app/assets/javascripts/alert_management/components/alert_details.vue @@ -35,13 +35,24 @@ export default { errorMsg: s__( 'AlertManagement|There was an error displaying the alert. Please refresh the page to try again.', ), - fullAlertDetailsTitle: s__('AlertManagement|Alert details'), - overviewTitle: s__('AlertManagement|Overview'), - metricsTitle: s__('AlertManagement|Metrics'), reportedAt: s__('AlertManagement|Reported %{when}'), reportedAtWithTool: s__('AlertManagement|Reported %{when} by %{tool}'), }, severityLabels: ALERTS_SEVERITY_LABELS, + tabsConfig: [ + { + id: 'overview', + title: s__('AlertManagement|Overview'), + }, + { + id: 'fullDetails', + title: s__('AlertManagement|Alert details'), + }, + { + id: 'metrics', + title: s__('AlertManagement|Metrics'), + }, + ], components: { GlBadge, GlAlert, @@ -102,8 +113,8 @@ export default { errored: false, sidebarStatus: false, isErrorDismissed: false, - createIssueError: '', - issueCreationInProgress: false, + createIncidentError: '', + incidentCreationInProgress: false, sidebarErrorMessage: '', }; }, @@ -119,6 +130,18 @@ export default { showErrorMsg() { return this.errored && !this.isErrorDismissed; }, + activeTab() { + return this.$route.params.tabId || this.$options.tabsConfig[0].id; + }, + currentTabIndex: { + get() { + return this.$options.tabsConfig.findIndex(tab => tab.id === this.activeTab); + }, + set(tabIdx) { + const tabId = this.$options.tabsConfig[tabIdx].id; + this.$router.replace({ name: 'tab', params: { tabId } }); + }, + }, }, mounted() { this.trackPageViews(); @@ -149,8 +172,8 @@ export default { this.errored = true; this.sidebarErrorMessage = errorMessage; }, - createIssue() { - this.issueCreationInProgress = true; + createIncident() { + this.incidentCreationInProgress = true; this.$apollo .mutate({ @@ -162,18 +185,18 @@ export default { }) .then(({ data: { createAlertIssue: { errors, issue } } }) => { if (errors?.length) { - [this.createIssueError] = errors; - this.issueCreationInProgress = false; + [this.createIncidentError] = errors; + this.incidentCreationInProgress = false; } else if (issue) { - visitUrl(this.issuePath(issue.iid)); + visitUrl(this.incidentPath(issue.iid)); } }) .catch(error => { - this.createIssueError = error; - this.issueCreationInProgress = false; + this.createIncidentError = error; + this.incidentCreationInProgress = false; }); }, - issuePath(issueId) { + incidentPath(issueId) { return joinPaths(this.projectIssuesPath, issueId); }, trackPageViews() { @@ -190,12 +213,12 @@ export default { <p v-html="sidebarErrorMessage || $options.i18n.errorMsg"></p> </gl-alert> <gl-alert - v-if="createIssueError" + v-if="createIncidentError" variant="danger" - data-testid="issueCreationError" - @dismiss="createIssueError = null" + data-testid="incidentCreationError" + @dismiss="createIncidentError = null" > - {{ createIssueError }} + {{ createIncidentError }} </gl-alert> <div v-if="loading"><gl-loading-icon size="lg" class="gl-mt-5" /></div> <div @@ -204,19 +227,12 @@ export default { :class="{ 'pr-sm-8': sidebarStatus }" > <div - class="gl-display-flex gl-justify-content-space-between gl-align-items-baseline gl-px-1 py-3 py-md-4 gl-border-b-1 gl-border-b-gray-100 gl-border-b-solid flex-column flex-sm-row" + class="gl-display-flex gl-justify-content-space-between gl-align-items-center gl-px-1 py-3 py-md-4 gl-border-b-1 gl-border-b-gray-100 gl-border-b-solid gl-flex-direction-column gl-sm-flex-direction-row" > - <div - data-testid="alert-header" - class="gl-display-flex gl-align-items-center gl-justify-content-center" - > - <div - class="gl-display-inline-flex gl-align-items-center gl-justify-content-space-between" - > - <gl-badge class="gl-mr-3"> - <strong>{{ s__('AlertManagement|Alert') }}</strong> - </gl-badge> - </div> + <div data-testid="alert-header"> + <gl-badge class="gl-mr-3"> + <strong>{{ s__('AlertManagement|Alert') }}</strong> + </gl-badge> <span> <gl-sprintf :message="reportedAtMessage"> <template #when> @@ -228,24 +244,24 @@ export default { </div> <gl-button v-if="alert.issueIid" - class="gl-mt-3 mt-sm-0 align-self-center align-self-sm-baseline alert-details-issue-button" - data-testid="viewIssueBtn" - :href="issuePath(alert.issueIid)" + class="gl-mt-3 mt-sm-0 align-self-center align-self-sm-baseline alert-details-incident-button" + data-testid="viewIncidentBtn" + :href="incidentPath(alert.issueIid)" category="primary" variant="success" > - {{ s__('AlertManagement|View issue') }} + {{ s__('AlertManagement|View incident') }} </gl-button> <gl-button v-else - class="gl-mt-3 mt-sm-0 align-self-center align-self-sm-baseline alert-details-issue-button" - data-testid="createIssueBtn" - :loading="issueCreationInProgress" + class="gl-mt-3 mt-sm-0 align-self-center align-self-sm-baseline alert-details-incident-button" + data-testid="createIncidentBtn" + :loading="incidentCreationInProgress" category="primary" variant="success" - @click="createIssue()" + @click="createIncident()" > - {{ s__('AlertManagement|Create issue') }} + {{ s__('AlertManagement|Create incident') }} </gl-button> <gl-button :aria-label="__('Toggle sidebar')" @@ -264,8 +280,8 @@ export default { > <h2 data-testid="title">{{ alert.title }}</h2> </div> - <gl-tabs v-if="alert" data-testid="alertDetailsTabs"> - <gl-tab data-testid="overviewTab" :title="$options.i18n.overviewTitle"> + <gl-tabs v-if="alert" v-model="currentTabIndex" data-testid="alertDetailsTabs"> + <gl-tab :data-testid="$options.tabsConfig[0].id" :title="$options.tabsConfig[0].title"> <div v-if="alert.severity" class="gl-mt-3 gl-mb-5 gl-display-flex"> <div class="gl-font-weight-bold gl-w-13 gl-text-right gl-pr-3"> {{ s__('AlertManagement|Severity') }}: @@ -308,6 +324,12 @@ export default { </div> <div class="gl-pl-2" data-testid="service">{{ alert.service }}</div> </div> + <div v-if="alert.runbook" class="gl-my-5 gl-display-flex"> + <div class="bold gl-w-13 gl-text-right gl-pr-3"> + {{ s__('AlertManagement|Runbook') }}: + </div> + <div class="gl-pl-2" data-testid="runbook">{{ alert.runbook }}</div> + </div> <template> <div v-if="alert.notes.nodes" class="issuable-discussion py-5"> <ul class="notes main-notes-list timeline"> @@ -316,7 +338,7 @@ export default { </div> </template> </gl-tab> - <gl-tab data-testid="fullDetailsTab" :title="$options.i18n.fullAlertDetailsTitle"> + <gl-tab :data-testid="$options.tabsConfig[1].id" :title="$options.tabsConfig[1].title"> <gl-table class="alert-management-details-table" :items="[{ key: 'Value', ...alert }]" @@ -332,7 +354,7 @@ export default { </template> </gl-table> </gl-tab> - <gl-tab data-testId="metricsTab" :title="$options.i18n.metricsTitle"> + <gl-tab :data-testid="$options.tabsConfig[2].id" :title="$options.tabsConfig[2].title"> <alert-metrics :dashboard-url="alert.metricsDashboardUrl" /> </gl-tab> </gl-tabs> diff --git a/app/assets/javascripts/alert_management/components/alert_management_empty_state.vue b/app/assets/javascripts/alert_management/components/alert_management_empty_state.vue index 13b6a8e6653..68443166f40 100644 --- a/app/assets/javascripts/alert_management/components/alert_management_empty_state.vue +++ b/app/assets/javascripts/alert_management/components/alert_management_empty_state.vue @@ -1,6 +1,7 @@ <script> -import { GlEmptyState, GlButton } from '@gitlab/ui'; +import { GlEmptyState, GlButton, GlLink } from '@gitlab/ui'; import { s__ } from '~/locale'; +import alertsHelpUrlQuery from '../graphql/queries/alert_help_url.query.graphql'; export default { i18n: { @@ -25,6 +26,12 @@ export default { components: { GlEmptyState, GlButton, + GlLink, + }, + apollo: { + alertsHelpUrl: { + query: alertsHelpUrlQuery, + }, }, props: { enableAlertManagementPath: { @@ -50,6 +57,11 @@ export default { default: '', }, }, + data() { + return { + alertsHelpUrl: '', + }; + }, computed: { emptyState() { return { @@ -71,13 +83,9 @@ export default { <template #description> <div class="gl-display-block"> <span>{{ emptyState.info }}</span> - <a - v-if="!opsgenieMvcEnabled" - href="/help/user/project/operations/alert_management.html" - target="_blank" - > + <gl-link v-if="!opsgenieMvcEnabled" :href="alertsHelpUrl" target="_blank"> {{ $options.i18n.moreInformation }} - </a> + </gl-link> </div> <div v-if="alertsCanBeEnabled" class="gl-display-block center gl-pt-4"> <gl-button category="primary" variant="success" :href="emptyState.link"> diff --git a/app/assets/javascripts/alert_management/components/alert_management_table.vue b/app/assets/javascripts/alert_management/components/alert_management_table.vue index 7dd3d7b5dc3..92fd85c6217 100644 --- a/app/assets/javascripts/alert_management/components/alert_management_table.vue +++ b/app/assets/javascripts/alert_management/components/alert_management_table.vue @@ -12,8 +12,8 @@ import { GlSearchBoxByType, GlSprintf, } from '@gitlab/ui'; -import { __, s__ } from '~/locale'; import { debounce, trim } from 'lodash'; +import { __, s__ } from '~/locale'; import { joinPaths, visitUrl } from '~/lib/utils/url_utility'; import { fetchPolicies } from '~/lib/graphql'; import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue'; @@ -60,15 +60,15 @@ export default { { key: 'severity', label: s__('AlertManagement|Severity'), - tdClass: `${tdClass} rounded-top text-capitalize`, thClass: `${thClass} gl-w-eighth`, + tdClass: `${tdClass} rounded-top text-capitalize sortable-cell`, sortable: true, }, { key: 'startedAt', label: s__('AlertManagement|Start time'), thClass: `${thClass} js-started-at w-15p`, - tdClass, + tdClass: `${tdClass} sortable-cell`, sortable: true, }, { @@ -81,7 +81,7 @@ export default { key: 'eventCount', label: s__('AlertManagement|Events'), thClass: `${thClass} text-right gl-w-12`, - tdClass: `${tdClass} text-md-right`, + tdClass: `${tdClass} text-md-right sortable-cell`, sortable: true, }, { @@ -89,7 +89,6 @@ export default { label: s__('AlertManagement|Issue'), thClass: 'gl-w-12 gl-pointer-events-none', tdClass, - sortable: false, }, { key: 'assignees', @@ -99,9 +98,9 @@ export default { }, { key: 'status', - thClass: `${thClass} w-15p`, label: s__('AlertManagement|Status'), - tdClass: `${tdClass} rounded-bottom`, + thClass: `${thClass} w-15p`, + tdClass: `${tdClass} rounded-bottom sortable-cell`, sortable: true, }, ], @@ -169,7 +168,7 @@ export default { }; }, error() { - this.errored = true; + this.hasError = true; }, }, alertsCount: { @@ -188,10 +187,9 @@ export default { data() { return { searchTerm: '', - errored: false, + hasError: false, errorMessage: '', isAlertDismissed: false, - isErrorAlertDismissed: false, sort: 'STARTED_AT_DESC', statusFilter: [], filteredByStatus: '', @@ -204,16 +202,13 @@ export default { computed: { showNoAlertsMsg() { return ( - !this.errored && + !this.hasError && !this.loading && this.alertsCount?.all === 0 && !this.searchTerm && !this.isAlertDismissed ); }, - showErrorMsg() { - return this.errored && !this.isErrorAlertDismissed; - }, loading() { return this.$apollo.queries.alerts.loading; }, @@ -307,11 +302,11 @@ export default { }; }, handleAlertError(errorMessage) { - this.errored = true; + this.hasError = true; this.errorMessage = errorMessage; }, dismissError() { - this.isErrorAlertDismissed = true; + this.hasError = false; this.errorMessage = ''; }, }, @@ -319,7 +314,7 @@ export default { </script> <template> <div> - <div class="alert-management-list"> + <div class="incident-management-list"> <gl-alert v-if="showNoAlertsMsg" @dismiss="isAlertDismissed = true"> <gl-sprintf :message="$options.i18n.noAlertsMsg"> <template #link="{ content }"> @@ -333,16 +328,14 @@ export default { </template> </gl-sprintf> </gl-alert> - <gl-alert - v-if="showErrorMsg" - variant="danger" - data-testid="alert-error" - @dismiss="dismissError" - > + <gl-alert v-if="hasError" variant="danger" data-testid="alert-error" @dismiss="dismissError"> <p v-html="errorMessage || $options.i18n.errorMsg"></p> </gl-alert> - <gl-tabs content-class="gl-p-0" @input="filterAlertsByStatus"> + <gl-tabs + content-class="gl-p-0 gl-border-b-solid gl-border-b-1 gl-border-gray-100" + @input="filterAlertsByStatus" + > <gl-tab v-for="tab in $options.statusTabs" :key="tab.status"> <template slot="title"> <span>{{ tab.title }}</span> diff --git a/app/assets/javascripts/alert_management/components/alert_status.vue b/app/assets/javascripts/alert_management/components/alert_status.vue index 9b726fe2944..8531ca1374e 100644 --- a/app/assets/javascripts/alert_management/components/alert_status.vue +++ b/app/assets/javascripts/alert_management/components/alert_status.vue @@ -1,5 +1,5 @@ <script> -import { GlDropdown, GlDropdownItem, GlButton } from '@gitlab/ui'; +import { GlDeprecatedDropdown, GlDeprecatedDropdownItem, GlButton } from '@gitlab/ui'; import { s__ } from '~/locale'; import Tracking from '~/tracking'; import { trackAlertStatusUpdateOptions } from '../constants'; @@ -18,8 +18,8 @@ export default { RESOLVED: s__('AlertManagement|Resolved'), }, components: { - GlDropdown, - GlDropdownItem, + GlDeprecatedDropdown, + GlDeprecatedDropdownItem, GlButton, }, props: { @@ -91,7 +91,7 @@ export default { <template> <div class="dropdown dropdown-menu-selectable" :class="dropdownClass"> - <gl-dropdown + <gl-deprecated-dropdown ref="dropdown" right :text="$options.statuses[alert.status]" @@ -112,7 +112,7 @@ export default { /> </div> <div class="dropdown-content dropdown-body"> - <gl-dropdown-item + <gl-deprecated-dropdown-item v-for="(label, field) in $options.statuses" :key="field" data-testid="statusDropdownItem" @@ -122,8 +122,8 @@ export default { @click="updateAlertStatus(label)" > {{ label }} - </gl-dropdown-item> + </gl-deprecated-dropdown-item> </div> - </gl-dropdown> + </gl-deprecated-dropdown> </div> </template> diff --git a/app/assets/javascripts/alert_management/components/sidebar/sidebar_assignee.vue b/app/assets/javascripts/alert_management/components/sidebar/sidebar_assignee.vue index df07038151e..0a1478ef5fe 100644 --- a/app/assets/javascripts/alert_management/components/sidebar/sidebar_assignee.vue +++ b/app/assets/javascripts/alert_management/components/sidebar/sidebar_assignee.vue @@ -1,9 +1,9 @@ <script> -import { GlDropdownItem } from '@gitlab/ui'; +import { GlDeprecatedDropdownItem } from '@gitlab/ui'; export default { components: { - GlDropdownItem, + GlDeprecatedDropdownItem, }, props: { user: { @@ -24,7 +24,7 @@ export default { </script> <template> - <gl-dropdown-item + <gl-deprecated-dropdown-item :key="user.username" data-testid="assigneeDropdownItem" class="assignee-dropdown-item gl-vertical-align-middle" @@ -47,5 +47,5 @@ export default { </strong> <span class="dropdown-menu-user-username"> {{ user.username }}</span> </span> - </gl-dropdown-item> + </gl-deprecated-dropdown-item> </template> diff --git a/app/assets/javascripts/alert_management/components/sidebar/sidebar_assignees.vue b/app/assets/javascripts/alert_management/components/sidebar/sidebar_assignees.vue index cb32a5ffd4f..4af5c83b30c 100644 --- a/app/assets/javascripts/alert_management/components/sidebar/sidebar_assignees.vue +++ b/app/assets/javascripts/alert_management/components/sidebar/sidebar_assignees.vue @@ -1,20 +1,20 @@ <script> import { GlIcon, - GlDropdown, - GlDropdownDivider, - GlDropdownHeader, - GlDropdownItem, + GlDeprecatedDropdown, + GlDeprecatedDropdownDivider, + GlDeprecatedDropdownHeader, + GlDeprecatedDropdownItem, GlLoadingIcon, GlTooltip, GlButton, GlSprintf, } from '@gitlab/ui'; +import { debounce } from 'lodash'; import axios from '~/lib/utils/axios_utils'; import { s__, __ } from '~/locale'; import alertSetAssignees from '../../graphql/mutations/alert_set_assignees.mutation.graphql'; import SidebarAssignee from './sidebar_assignee.vue'; -import { debounce } from 'lodash'; const DATA_REFETCH_DELAY = 250; @@ -33,10 +33,10 @@ export default { }, components: { GlIcon, - GlDropdown, - GlDropdownItem, - GlDropdownDivider, - GlDropdownHeader, + GlDeprecatedDropdown, + GlDeprecatedDropdownItem, + GlDeprecatedDropdownDivider, + GlDeprecatedDropdownHeader, GlLoadingIcon, GlTooltip, GlButton, @@ -213,7 +213,7 @@ export default { </p> <div class="dropdown dropdown-menu-selectable" :class="dropdownClass"> - <gl-dropdown + <gl-deprecated-dropdown ref="dropdown" :text="assignedUser" class="w-100" @@ -243,18 +243,18 @@ export default { </div> <div class="dropdown-content dropdown-body"> <template v-if="userListValid"> - <gl-dropdown-item + <gl-deprecated-dropdown-item :active="!userName" active-class="is-active" @click="updateAlertAssignees('')" > {{ __('Unassigned') }} - </gl-dropdown-item> - <gl-dropdown-divider /> + </gl-deprecated-dropdown-item> + <gl-deprecated-dropdown-divider /> - <gl-dropdown-header class="mt-0"> + <gl-deprecated-dropdown-header class="mt-0"> {{ __('Assignee') }} - </gl-dropdown-header> + </gl-deprecated-dropdown-header> <sidebar-assignee v-for="user in sortedUsers" :key="user.username" @@ -263,17 +263,17 @@ export default { @update-alert-assignees="updateAlertAssignees" /> </template> - <gl-dropdown-item v-else-if="userListEmpty"> + <gl-deprecated-dropdown-item v-else-if="userListEmpty"> {{ __('No Matching Results') }} - </gl-dropdown-item> + </gl-deprecated-dropdown-item> <gl-loading-icon v-else /> </div> - </gl-dropdown> + </gl-deprecated-dropdown> </div> <gl-loading-icon v-if="isUpdating" :inline="true" /> <p v-else-if="!isDropdownShowing" class="value gl-m-0" :class="{ 'no-value': !userName }"> - <span v-if="userName" class="gl-text-gray-700" data-testid="assigned-users">{{ + <span v-if="userName" class="gl-text-gray-500" data-testid="assigned-users">{{ assignedUser }}</span> <span v-else class="gl-display-flex gl-align-items-center"> diff --git a/app/assets/javascripts/alert_management/components/sidebar/sidebar_header.vue b/app/assets/javascripts/alert_management/components/sidebar/sidebar_header.vue index fd40b5d9f65..70902a204f8 100644 --- a/app/assets/javascripts/alert_management/components/sidebar/sidebar_header.vue +++ b/app/assets/javascripts/alert_management/components/sidebar/sidebar_header.vue @@ -27,7 +27,7 @@ export default { <template> <div class="block gl-display-flex gl-justify-content-space-between"> <span class="issuable-header-text hide-collapsed"> - {{ __('To Do') }} + {{ __('To-Do') }} </span> <sidebar-todo v-if="!sidebarCollapsed" diff --git a/app/assets/javascripts/alert_management/components/sidebar/sidebar_status.vue b/app/assets/javascripts/alert_management/components/sidebar/sidebar_status.vue index 44a81aba828..0a2bad5510b 100644 --- a/app/assets/javascripts/alert_management/components/sidebar/sidebar_status.vue +++ b/app/assets/javascripts/alert_management/components/sidebar/sidebar_status.vue @@ -107,7 +107,7 @@ export default { > <span v-if="$options.statuses[alert.status]" - class="gl-text-gray-700" + class="gl-text-gray-500" data-testid="status" >{{ $options.statuses[alert.status] }}</span > diff --git a/app/assets/javascripts/alert_management/components/sidebar/sidebar_todo.vue b/app/assets/javascripts/alert_management/components/sidebar/sidebar_todo.vue index 7d3135ad50d..5bd69a1f0ec 100644 --- a/app/assets/javascripts/alert_management/components/sidebar/sidebar_todo.vue +++ b/app/assets/javascripts/alert_management/components/sidebar/sidebar_todo.vue @@ -1,13 +1,14 @@ <script> import { s__ } from '~/locale'; import Todo from '~/sidebar/components/todo_toggle/todo.vue'; -import axios from '~/lib/utils/axios_utils'; -import createAlertTodo from '../../graphql/mutations/alert_todo_create.graphql'; +import createAlertTodo from '../../graphql/mutations/alert_todo_create.mutation.graphql'; +import todoMarkDone from '../../graphql/mutations/alert_todo_mark_done.mutation.graphql'; +import alertQuery from '../../graphql/queries/details.query.graphql'; export default { i18n: { UPDATE_ALERT_TODO_ERROR: s__( - 'AlertManagement|There was an error while updating the To Do of the alert.', + 'AlertManagement|There was an error while updating the To-Do of the alert.', ), }, components: { @@ -30,14 +31,24 @@ export default { data() { return { isUpdating: false, - isTodo: false, - todo: '', }; }, computed: { alertID() { return parseInt(this.alert.iid, 10); }, + firstToDoId() { + return this.alert?.todos?.nodes[0]?.id; + }, + hasPendingTodos() { + return this.alert?.todos?.nodes.length > 0; + }, + getAlertQueryVariables() { + return { + fullPath: this.projectPath, + alertId: this.alert.iid, + }; + }, }, methods: { updateToDoCount(add) { @@ -51,11 +62,7 @@ export default { return document.dispatchEvent(headerTodoEvent); }, - toggleTodo() { - if (this.todo) { - return this.markAsDone(); - } - + addToDo() { this.isUpdating = true; return this.$apollo .mutate({ @@ -65,24 +72,14 @@ export default { projectPath: this.projectPath, }, }) - .then(({ data: { alertTodoCreate: { todo = {}, errors = [] } } = {} } = {}) => { + .then(({ data: { errors = [] } }) => { if (errors[0]) { - return this.$emit( - 'alert-error', - `${this.$options.i18n.UPDATE_ALERT_TODO_ERROR} ${errors[0]}.`, - ); + return this.throwError(errors[0]); } - - this.todo = todo.id; return this.updateToDoCount(true); }) .catch(() => { - this.$emit( - 'alert-error', - `${this.$options.i18n.UPDATE_ALERT_TODO_ERROR} ${s__( - 'AlertManagement|Please try again.', - )}`, - ); + this.throwError(); }) .finally(() => { this.isUpdating = false; @@ -90,20 +87,45 @@ export default { }, markAsDone() { this.isUpdating = true; - - return axios - .delete(`/dashboard/todos/${this.todo.split('/').pop()}`) - .then(() => { - this.todo = ''; + return this.$apollo + .mutate({ + mutation: todoMarkDone, + variables: { + id: this.firstToDoId, + }, + update: this.updateCache, + }) + .then(({ data: { errors = [] } }) => { + if (errors[0]) { + return this.throwError(errors[0]); + } return this.updateToDoCount(false); }) .catch(() => { - this.$emit('alert-error', this.$options.i18n.UPDATE_ALERT_TODO_ERROR); + this.throwError(); }) .finally(() => { this.isUpdating = false; }); }, + updateCache(store) { + const data = store.readQuery({ + query: alertQuery, + variables: this.getAlertQueryVariables, + }); + + data.project.alertManagementAlerts.nodes[0].todos.nodes.shift(); + + store.writeQuery({ + query: alertQuery, + variables: this.getAlertQueryVariables, + data, + }); + }, + throwError(err = '') { + const error = err || s__('AlertManagement|Please try again.'); + this.$emit('alert-error', `${this.$options.i18n.UPDATE_ALERT_TODO_ERROR} ${error}`); + }, }, }; </script> @@ -114,10 +136,10 @@ export default { data-testid="alert-todo-button" :collapsed="sidebarCollapsed" :issuable-id="alertID" - :is-todo="todo !== ''" + :is-todo="hasPendingTodos" :is-action-active="isUpdating" issuable-type="alert" - @toggleTodo="toggleTodo" + @toggleTodo="hasPendingTodos ? markAsDone() : addToDo()" /> </div> </template> diff --git a/app/assets/javascripts/alert_management/details.js b/app/assets/javascripts/alert_management/details.js index 2820bcb9665..dccf990f0b4 100644 --- a/app/assets/javascripts/alert_management/details.js +++ b/app/assets/javascripts/alert_management/details.js @@ -1,7 +1,8 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; -import createDefaultClient from '~/lib/graphql'; import { defaultDataIdFromObject } from 'apollo-cache-inmemory'; +import createDefaultClient from '~/lib/graphql'; +import createRouter from './router'; import AlertDetails from './components/alert_details.vue'; import sidebarStatusQuery from './graphql/queries/sidebar_status.query.graphql'; @@ -10,6 +11,7 @@ Vue.use(VueApollo); export default selector => { const domEl = document.querySelector(selector); const { alertId, projectPath, projectIssuesPath, projectId } = domEl.dataset; + const router = createRouter(); const resolvers = { Mutation: { @@ -54,6 +56,7 @@ export default selector => { components: { AlertDetails, }, + router, render(createElement) { return createElement('alert-details', {}); }, diff --git a/app/assets/javascripts/alert_management/graphql/fragments/detail_item.fragment.graphql b/app/assets/javascripts/alert_management/graphql/fragments/detail_item.fragment.graphql index 18fab429164..0712ff12c23 100644 --- a/app/assets/javascripts/alert_management/graphql/fragments/detail_item.fragment.graphql +++ b/app/assets/javascripts/alert_management/graphql/fragments/detail_item.fragment.graphql @@ -11,6 +11,12 @@ fragment AlertDetailItem on AlertManagementAlert { updatedAt endedAt details + runbook + todos { + nodes { + id + } + } notes { nodes { ...AlertNote diff --git a/app/assets/javascripts/alert_management/graphql/mutations/alert_todo_create.graphql b/app/assets/javascripts/alert_management/graphql/mutations/alert_todo_create.graphql deleted file mode 100644 index cdf3d763302..00000000000 --- a/app/assets/javascripts/alert_management/graphql/mutations/alert_todo_create.graphql +++ /dev/null @@ -1,11 +0,0 @@ -mutation($projectPath: ID!, $iid: String!) { - alertTodoCreate(input: { iid: $iid, projectPath: $projectPath }) { - errors - alert { - iid - } - todo { - id - } - } -} diff --git a/app/assets/javascripts/alert_management/graphql/mutations/alert_todo_create.mutation.graphql b/app/assets/javascripts/alert_management/graphql/mutations/alert_todo_create.mutation.graphql new file mode 100644 index 00000000000..ac9858c104f --- /dev/null +++ b/app/assets/javascripts/alert_management/graphql/mutations/alert_todo_create.mutation.graphql @@ -0,0 +1,10 @@ +#import "../fragments/detail_item.fragment.graphql" + +mutation alertTodoCreate($projectPath: ID!, $iid: String!) { + alertTodoCreate(input: { iid: $iid, projectPath: $projectPath }) { + errors + alert { + ...AlertDetailItem + } + } +} diff --git a/app/assets/javascripts/alert_management/graphql/mutations/alert_todo_mark_done.mutation.graphql b/app/assets/javascripts/alert_management/graphql/mutations/alert_todo_mark_done.mutation.graphql new file mode 100644 index 00000000000..4d59b4d94cd --- /dev/null +++ b/app/assets/javascripts/alert_management/graphql/mutations/alert_todo_mark_done.mutation.graphql @@ -0,0 +1,8 @@ +mutation todoMarkDone($id: ID!) { + todoMarkDone(input: { id: $id }) { + errors + todo { + id + } + } +} diff --git a/app/assets/javascripts/alert_management/graphql/queries/alert_help_url.query.graphql b/app/assets/javascripts/alert_management/graphql/queries/alert_help_url.query.graphql new file mode 100644 index 00000000000..05a8bc7c736 --- /dev/null +++ b/app/assets/javascripts/alert_management/graphql/queries/alert_help_url.query.graphql @@ -0,0 +1,3 @@ +query alertsHelpUrl { + alertsHelpUrl @client +} diff --git a/app/assets/javascripts/alert_management/list.js b/app/assets/javascripts/alert_management/list.js index 3f78ca66a59..e180ab5f7e3 100644 --- a/app/assets/javascripts/alert_management/list.js +++ b/app/assets/javascripts/alert_management/list.js @@ -1,7 +1,7 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; -import createDefaultClient from '~/lib/graphql'; import { defaultDataIdFromObject } from 'apollo-cache-inmemory'; +import createDefaultClient from '~/lib/graphql'; import { parseBoolean } from '~/lib/utils/common_utils'; import AlertManagementList from './components/alert_management_list_wrapper.vue'; @@ -16,6 +16,7 @@ export default () => { enableAlertManagementPath, emptyAlertSvgPath, populatingAlertsHelpUrl, + alertsHelpUrl, opsgenieMvcTargetUrl, } = domEl.dataset; let { alertManagementEnabled, userCanEnableAlertManagement, opsgenieMvcEnabled } = domEl.dataset; @@ -41,6 +42,12 @@ export default () => { ), }); + apolloProvider.clients.defaultClient.cache.writeData({ + data: { + alertsHelpUrl, + }, + }); + return new Vue({ el: selector, apolloProvider, diff --git a/app/assets/javascripts/alert_management/router.js b/app/assets/javascripts/alert_management/router.js new file mode 100644 index 00000000000..5687fe4e0f5 --- /dev/null +++ b/app/assets/javascripts/alert_management/router.js @@ -0,0 +1,13 @@ +import Vue from 'vue'; +import VueRouter from 'vue-router'; +import { joinPaths } from '~/lib/utils/url_utility'; + +Vue.use(VueRouter); + +export default function createRouter(base) { + return new VueRouter({ + mode: 'hash', + base: joinPaths(gon.relative_url_root || '', base), + routes: [{ path: '/:tabId', name: 'tab' }], + }); +} |