summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/alert_management/components
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/alert_management/components')
-rw-r--r--app/assets/javascripts/alert_management/components/alert_details.vue380
-rw-r--r--app/assets/javascripts/alert_management/components/alert_management_table.vue10
-rw-r--r--app/assets/javascripts/alert_management/components/alert_metrics.vue56
-rw-r--r--app/assets/javascripts/alert_management/components/alert_sidebar.vue82
-rw-r--r--app/assets/javascripts/alert_management/components/alert_status.vue119
-rw-r--r--app/assets/javascripts/alert_management/components/alert_summary_row.vue18
-rw-r--r--app/assets/javascripts/alert_management/components/sidebar/sidebar_assignee.vue38
-rw-r--r--app/assets/javascripts/alert_management/components/sidebar/sidebar_assignees.vue299
-rw-r--r--app/assets/javascripts/alert_management/components/sidebar/sidebar_header.vue41
-rw-r--r--app/assets/javascripts/alert_management/components/sidebar/sidebar_status.vue120
-rw-r--r--app/assets/javascripts/alert_management/components/sidebar/sidebar_todo.vue149
-rw-r--r--app/assets/javascripts/alert_management/components/system_notes/system_note.vue48
12 files changed, 3 insertions, 1357 deletions
diff --git a/app/assets/javascripts/alert_management/components/alert_details.vue b/app/assets/javascripts/alert_management/components/alert_details.vue
deleted file mode 100644
index 895c6e76019..00000000000
--- a/app/assets/javascripts/alert_management/components/alert_details.vue
+++ /dev/null
@@ -1,380 +0,0 @@
-<script>
-import {
- GlAlert,
- GlBadge,
- GlIcon,
- GlLink,
- GlLoadingIcon,
- GlSprintf,
- GlTabs,
- GlTab,
- GlButton,
- GlSafeHtmlDirective,
-} from '@gitlab/ui';
-import * as Sentry from '~/sentry/wrapper';
-import { s__ } from '~/locale';
-import alertQuery from '../graphql/queries/details.query.graphql';
-import sidebarStatusQuery from '../graphql/queries/sidebar_status.query.graphql';
-import { fetchPolicies } from '~/lib/graphql';
-import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
-import highlightCurrentUser from '~/behaviors/markdown/highlight_current_user';
-import initUserPopovers from '~/user_popovers';
-import { ALERTS_SEVERITY_LABELS, trackAlertsDetailsViewsOptions } from '../constants';
-import createIssueMutation from '../graphql/mutations/create_issue_from_alert.mutation.graphql';
-import toggleSidebarStatusMutation from '../graphql/mutations/toggle_sidebar_status.mutation.graphql';
-import { visitUrl, joinPaths } from '~/lib/utils/url_utility';
-import Tracking from '~/tracking';
-import { toggleContainerClasses } from '~/lib/utils/dom_utils';
-import SystemNote from './system_notes/system_note.vue';
-import AlertSidebar from './alert_sidebar.vue';
-import AlertMetrics from './alert_metrics.vue';
-import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue';
-import AlertSummaryRow from './alert_summary_row.vue';
-
-const containerEl = document.querySelector('.page-with-contextual-sidebar');
-
-export default {
- i18n: {
- errorMsg: s__(
- 'AlertManagement|There was an error displaying the alert. Please refresh the page to try again.',
- ),
- reportedAt: s__('AlertManagement|Reported %{when}'),
- reportedAtWithTool: s__('AlertManagement|Reported %{when} by %{tool}'),
- },
- directives: {
- SafeHtml: GlSafeHtmlDirective,
- },
- severityLabels: ALERTS_SEVERITY_LABELS,
- tabsConfig: [
- {
- id: 'overview',
- title: s__('AlertManagement|Alert details'),
- },
- {
- id: 'metrics',
- title: s__('AlertManagement|Metrics'),
- },
- {
- id: 'activity',
- title: s__('AlertManagement|Activity feed'),
- },
- ],
- components: {
- AlertDetailsTable,
- AlertSummaryRow,
- GlBadge,
- GlAlert,
- GlIcon,
- GlLink,
- GlLoadingIcon,
- GlSprintf,
- GlTab,
- GlTabs,
- GlButton,
- TimeAgoTooltip,
- AlertSidebar,
- SystemNote,
- AlertMetrics,
- },
- inject: {
- projectPath: {
- default: '',
- },
- alertId: {
- default: '',
- },
- projectId: {
- default: '',
- },
- projectIssuesPath: {
- default: '',
- },
- },
- apollo: {
- alert: {
- fetchPolicy: fetchPolicies.CACHE_AND_NETWORK,
- query: alertQuery,
- variables() {
- return {
- fullPath: this.projectPath,
- alertId: this.alertId,
- };
- },
- update(data) {
- return data?.project?.alertManagementAlerts?.nodes?.[0] ?? null;
- },
- error(error) {
- this.errored = true;
- Sentry.captureException(error);
- },
- },
- sidebarStatus: {
- query: sidebarStatusQuery,
- },
- },
- data() {
- return {
- alert: null,
- errored: false,
- sidebarStatus: false,
- isErrorDismissed: false,
- createIncidentError: '',
- incidentCreationInProgress: false,
- sidebarErrorMessage: '',
- };
- },
- computed: {
- loading() {
- return this.$apollo.queries.alert.loading;
- },
- reportedAtMessage() {
- return this.alert?.monitoringTool
- ? this.$options.i18n.reportedAtWithTool
- : this.$options.i18n.reportedAt;
- },
- 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 } });
- },
- },
- environmentName() {
- return this.alert?.environment?.name;
- },
- environmentPath() {
- return this.alert?.environment?.path;
- },
- },
- mounted() {
- this.trackPageViews();
- toggleContainerClasses(containerEl, {
- 'issuable-bulk-update-sidebar': true,
- 'right-sidebar-expanded': true,
- });
- },
- updated() {
- this.$nextTick(() => {
- highlightCurrentUser(this.$el.querySelectorAll('.gfm-project_member'));
- initUserPopovers(this.$el.querySelectorAll('.js-user-link'));
- });
- },
- methods: {
- dismissError() {
- this.isErrorDismissed = true;
- this.sidebarErrorMessage = '';
- },
- toggleSidebar() {
- this.$apollo.mutate({ mutation: toggleSidebarStatusMutation });
- toggleContainerClasses(containerEl, {
- 'right-sidebar-collapsed': !this.sidebarStatus,
- 'right-sidebar-expanded': this.sidebarStatus,
- });
- },
- handleAlertSidebarError(errorMessage) {
- this.errored = true;
- this.sidebarErrorMessage = errorMessage;
- },
- createIncident() {
- this.incidentCreationInProgress = true;
-
- this.$apollo
- .mutate({
- mutation: createIssueMutation,
- variables: {
- iid: this.alert.iid,
- projectPath: this.projectPath,
- },
- })
- .then(
- ({
- data: {
- createAlertIssue: { errors, issue },
- },
- }) => {
- if (errors?.length) {
- [this.createIncidentError] = errors;
- this.incidentCreationInProgress = false;
- } else if (issue) {
- visitUrl(this.incidentPath(issue.iid));
- }
- },
- )
- .catch((error) => {
- this.createIncidentError = error;
- this.incidentCreationInProgress = false;
- });
- },
- incidentPath(issueId) {
- return joinPaths(this.projectIssuesPath, issueId);
- },
- trackPageViews() {
- const { category, action } = trackAlertsDetailsViewsOptions;
- Tracking.event(category, action);
- },
- },
-};
-</script>
-
-<template>
- <div>
- <gl-alert v-if="showErrorMsg" variant="danger" @dismiss="dismissError">
- <p v-safe-html="sidebarErrorMessage || $options.i18n.errorMsg"></p>
- </gl-alert>
- <gl-alert
- v-if="createIncidentError"
- variant="danger"
- data-testid="incidentCreationError"
- @dismiss="createIncidentError = null"
- >
- {{ createIncidentError }}
- </gl-alert>
- <div v-if="loading"><gl-loading-icon size="lg" class="gl-mt-5" /></div>
- <div
- v-if="alert"
- class="alert-management-details gl-relative"
- :class="{ 'pr-sm-8': sidebarStatus }"
- >
- <div
- 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">
- <gl-badge class="gl-mr-3">
- <strong>{{ s__('AlertManagement|Alert') }}</strong>
- </gl-badge>
- <span>
- <gl-sprintf :message="reportedAtMessage">
- <template #when>
- <time-ago-tooltip :time="alert.createdAt" />
- </template>
- <template #tool>{{ alert.monitoringTool }}</template>
- </gl-sprintf>
- </span>
- </div>
- <gl-button
- v-if="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 incident') }}
- </gl-button>
- <gl-button
- v-else
- 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="createIncident()"
- >
- {{ s__('AlertManagement|Create incident') }}
- </gl-button>
- <gl-button
- :aria-label="__('Toggle sidebar')"
- category="primary"
- variant="default"
- class="d-sm-none gl-absolute toggle-sidebar-mobile-button"
- type="button"
- icon="chevron-double-lg-left"
- @click="toggleSidebar"
- />
- </div>
- <div
- v-if="alert"
- class="gl-display-flex gl-justify-content-space-between gl-align-items-center"
- >
- <h2 data-testid="title">{{ alert.title }}</h2>
- </div>
- <gl-tabs v-if="alert" v-model="currentTabIndex" data-testid="alertDetailsTabs">
- <gl-tab :data-testid="$options.tabsConfig[0].id" :title="$options.tabsConfig[0].title">
- <alert-summary-row v-if="alert.severity" :label="`${s__('AlertManagement|Severity')}:`">
- <span data-testid="severity">
- <gl-icon
- class="gl-vertical-align-middle"
- :size="12"
- :name="`severity-${alert.severity.toLowerCase()}`"
- :class="`icon-${alert.severity.toLowerCase()}`"
- />
- {{ $options.severityLabels[alert.severity] }}
- </span>
- </alert-summary-row>
- <alert-summary-row
- v-if="environmentName"
- :label="`${s__('AlertManagement|Environment')}:`"
- >
- <gl-link
- v-if="environmentPath"
- class="gl-display-inline-block"
- data-testid="environmentPath"
- :href="environmentPath"
- >
- {{ environmentName }}
- </gl-link>
- <span v-else data-testid="environmentName">{{ environmentName }}</span>
- </alert-summary-row>
- <alert-summary-row
- v-if="alert.startedAt"
- :label="`${s__('AlertManagement|Start time')}:`"
- >
- <time-ago-tooltip data-testid="startTimeItem" :time="alert.startedAt" />
- </alert-summary-row>
- <alert-summary-row
- v-if="alert.eventCount"
- :label="`${s__('AlertManagement|Events')}:`"
- data-testid="eventCount"
- >
- {{ alert.eventCount }}
- </alert-summary-row>
- <alert-summary-row
- v-if="alert.monitoringTool"
- :label="`${s__('AlertManagement|Tool')}:`"
- data-testid="monitoringTool"
- >
- {{ alert.monitoringTool }}
- </alert-summary-row>
- <alert-summary-row
- v-if="alert.service"
- :label="`${s__('AlertManagement|Service')}:`"
- data-testid="service"
- >
- {{ alert.service }}
- </alert-summary-row>
- <alert-summary-row
- v-if="alert.runbook"
- :label="`${s__('AlertManagement|Runbook')}:`"
- data-testid="runbook"
- >
- {{ alert.runbook }}
- </alert-summary-row>
- <alert-details-table :alert="alert" :loading="loading" />
- </gl-tab>
- <gl-tab :data-testid="$options.tabsConfig[1].id" :title="$options.tabsConfig[1].title">
- <alert-metrics :dashboard-url="alert.metricsDashboardUrl" />
- </gl-tab>
- <gl-tab :data-testid="$options.tabsConfig[2].id" :title="$options.tabsConfig[2].title">
- <div v-if="alert.notes.nodes.length > 0" class="issuable-discussion">
- <ul class="notes main-notes-list timeline">
- <system-note v-for="note in alert.notes.nodes" :key="note.id" :note="note" />
- </ul>
- </div>
- </gl-tab>
- </gl-tabs>
- <alert-sidebar
- :alert="alert"
- @toggle-sidebar="toggleSidebar"
- @alert-error="handleAlertSidebarError"
- />
- </div>
- </div>
-</template>
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 2bad15faa85..dae52a530ac 100644
--- a/app/assets/javascripts/alert_management/components/alert_management_table.vue
+++ b/app/assets/javascripts/alert_management/components/alert_management_table.vue
@@ -23,14 +23,10 @@ import {
} from '~/vue_shared/components/paginated_table_with_search_and_tabs/constants';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import { convertToSnakeCase } from '~/lib/utils/text_utility';
+import AlertStatus from '~/vue_shared/alert_details/components/alert_status.vue';
import getAlertsQuery from '~/graphql_shared/queries/get_alerts.query.graphql';
import getAlertsCountByStatus from '../graphql/queries/get_count_by_status.query.graphql';
-import {
- ALERTS_STATUS_TABS,
- ALERTS_SEVERITY_LABELS,
- trackAlertListViewsOptions,
-} from '../constants';
-import AlertStatus from './alert_status.vue';
+import { ALERTS_STATUS_TABS, SEVERITY_LEVELS, trackAlertListViewsOptions } from '../constants';
const TH_TEST_ID = { 'data-testid': 'alert-management-severity-sort' };
@@ -96,7 +92,7 @@ export default {
sortable: true,
},
],
- severityLabels: ALERTS_SEVERITY_LABELS,
+ severityLabels: SEVERITY_LEVELS,
statusTabs: ALERTS_STATUS_TABS,
components: {
GlAlert,
diff --git a/app/assets/javascripts/alert_management/components/alert_metrics.vue b/app/assets/javascripts/alert_management/components/alert_metrics.vue
deleted file mode 100644
index dd4faa03c00..00000000000
--- a/app/assets/javascripts/alert_management/components/alert_metrics.vue
+++ /dev/null
@@ -1,56 +0,0 @@
-<script>
-import Vue from 'vue';
-import Vuex from 'vuex';
-import * as Sentry from '~/sentry/wrapper';
-
-Vue.use(Vuex);
-
-export default {
- props: {
- dashboardUrl: {
- type: String,
- required: false,
- default: '',
- },
- },
- data() {
- return {
- metricEmbedComponent: null,
- namespace: 'alertMetrics',
- };
- },
- mounted() {
- if (this.dashboardUrl) {
- Promise.all([
- import('~/monitoring/components/embeds/metric_embed.vue'),
- import('~/monitoring/stores'),
- ])
- .then(([{ default: MetricEmbed }, { monitoringDashboard }]) => {
- this.$store = new Vuex.Store({
- modules: {
- [this.namespace]: monitoringDashboard,
- },
- });
- this.metricEmbedComponent = MetricEmbed;
- })
- .catch((e) => Sentry.captureException(e));
- }
- },
-};
-</script>
-
-<template>
- <div class="gl-py-3">
- <div v-if="dashboardUrl" ref="metricsChart">
- <component
- :is="metricEmbedComponent"
- v-if="metricEmbedComponent"
- :dashboard-url="dashboardUrl"
- :namespace="namespace"
- />
- </div>
- <div v-else ref="emptyState">
- {{ s__("AlertManagement|Metrics weren't available in the alerts payload.") }}
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/alert_management/components/alert_sidebar.vue b/app/assets/javascripts/alert_management/components/alert_sidebar.vue
deleted file mode 100644
index 41d77716592..00000000000
--- a/app/assets/javascripts/alert_management/components/alert_sidebar.vue
+++ /dev/null
@@ -1,82 +0,0 @@
-<script>
-import SidebarHeader from './sidebar/sidebar_header.vue';
-import SidebarTodo from './sidebar/sidebar_todo.vue';
-import SidebarStatus from './sidebar/sidebar_status.vue';
-import SidebarAssignees from './sidebar/sidebar_assignees.vue';
-
-import sidebarStatusQuery from '../graphql/queries/sidebar_status.query.graphql';
-
-export default {
- components: {
- SidebarAssignees,
- SidebarHeader,
- SidebarTodo,
- SidebarStatus,
- },
- inject: {
- projectPath: {
- default: '',
- },
- projectId: {
- default: '',
- },
- },
- props: {
- alert: {
- type: Object,
- required: true,
- },
- },
- apollo: {
- sidebarStatus: {
- query: sidebarStatusQuery,
- },
- },
- data() {
- return {
- sidebarStatus: false,
- };
- },
- computed: {
- sidebarCollapsedClass() {
- return this.sidebarStatus ? 'right-sidebar-collapsed' : 'right-sidebar-expanded';
- },
- },
-};
-</script>
-
-<template>
- <aside :class="sidebarCollapsedClass" class="right-sidebar alert-sidebar">
- <div class="issuable-sidebar js-issuable-update">
- <sidebar-header
- :sidebar-collapsed="sidebarStatus"
- :project-path="projectPath"
- :alert="alert"
- @toggle-sidebar="$emit('toggle-sidebar')"
- @alert-error="$emit('alert-error', $event)"
- />
- <sidebar-todo
- v-if="sidebarStatus"
- :project-path="projectPath"
- :alert="alert"
- :sidebar-collapsed="sidebarStatus"
- @alert-error="$emit('alert-error', $event)"
- />
- <sidebar-status
- :project-path="projectPath"
- :alert="alert"
- @toggle-sidebar="$emit('toggle-sidebar')"
- @alert-error="$emit('alert-error', $event)"
- />
- <sidebar-assignees
- :project-path="projectPath"
- :project-id="projectId"
- :alert="alert"
- :sidebar-collapsed="sidebarStatus"
- @toggle-sidebar="$emit('toggle-sidebar')"
- @alert-error="$emit('alert-error', $event)"
- />
- <div class="block"></div>
- </div>
- </aside>
-</template>
diff --git a/app/assets/javascripts/alert_management/components/alert_status.vue b/app/assets/javascripts/alert_management/components/alert_status.vue
deleted file mode 100644
index 2afdeb8b6fd..00000000000
--- a/app/assets/javascripts/alert_management/components/alert_status.vue
+++ /dev/null
@@ -1,119 +0,0 @@
-<script>
-import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
-import { s__ } from '~/locale';
-import Tracking from '~/tracking';
-import { trackAlertStatusUpdateOptions } from '../constants';
-import updateAlertStatusMutation from '~/graphql_shared/mutations/update_alert_status.mutation.graphql';
-
-export default {
- i18n: {
- UPDATE_ALERT_STATUS_ERROR: s__(
- 'AlertManagement|There was an error while updating the status of the alert.',
- ),
- UPDATE_ALERT_STATUS_INSTRUCTION: s__('AlertManagement|Please try again.'),
- },
- statuses: {
- TRIGGERED: s__('AlertManagement|Triggered'),
- ACKNOWLEDGED: s__('AlertManagement|Acknowledged'),
- RESOLVED: s__('AlertManagement|Resolved'),
- },
- components: {
- GlDropdown,
- GlDropdownItem,
- },
- props: {
- projectPath: {
- type: String,
- required: true,
- },
- alert: {
- type: Object,
- required: true,
- },
- isDropdownShowing: {
- type: Boolean,
- required: false,
- },
- isSidebar: {
- type: Boolean,
- required: true,
- },
- },
- computed: {
- dropdownClass() {
- // eslint-disable-next-line no-nested-ternary
- return this.isSidebar ? (this.isDropdownShowing ? 'show' : 'gl-display-none') : '';
- },
- },
- methods: {
- updateAlertStatus(status) {
- this.$emit('handle-updating', true);
- this.$apollo
- .mutate({
- mutation: updateAlertStatusMutation,
- variables: {
- iid: this.alert.iid,
- status: status.toUpperCase(),
- projectPath: this.projectPath,
- },
- })
- .then((resp) => {
- this.trackStatusUpdate(status);
- const errors = resp.data?.updateAlertStatus?.errors || [];
-
- if (errors[0]) {
- this.$emit(
- 'alert-error',
- `${this.$options.i18n.UPDATE_ALERT_STATUS_ERROR} ${errors[0]}`,
- );
- }
-
- this.$emit('hide-dropdown');
- })
- .catch(() => {
- this.$emit(
- 'alert-error',
- `${this.$options.i18n.UPDATE_ALERT_STATUS_ERROR} ${this.$options.i18n.UPDATE_ALERT_STATUS_INSTRUCTION}`,
- );
- })
- .finally(() => {
- this.$emit('handle-updating', false);
- });
- },
- trackStatusUpdate(status) {
- const { category, action, label } = trackAlertStatusUpdateOptions;
- Tracking.event(category, action, { label, property: status });
- },
- },
-};
-</script>
-
-<template>
- <div class="dropdown dropdown-menu-selectable" :class="dropdownClass">
- <gl-dropdown
- ref="dropdown"
- right
- :text="$options.statuses[alert.status]"
- class="w-100"
- toggle-class="dropdown-menu-toggle"
- @keydown.esc.native="$emit('hide-dropdown')"
- @hide="$emit('hide-dropdown')"
- >
- <p v-if="isSidebar" class="gl-new-dropdown-header-top" data-testid="dropdown-header">
- {{ s__('AlertManagement|Assign status') }}
- </p>
- <div class="dropdown-content dropdown-body">
- <gl-dropdown-item
- v-for="(label, field) in $options.statuses"
- :key="field"
- data-testid="statusDropdownItem"
- :active="label.toUpperCase() === alert.status"
- :active-class="'is-active'"
- @click="updateAlertStatus(label)"
- >
- {{ label }}
- </gl-dropdown-item>
- </div>
- </gl-dropdown>
- </div>
-</template>
diff --git a/app/assets/javascripts/alert_management/components/alert_summary_row.vue b/app/assets/javascripts/alert_management/components/alert_summary_row.vue
deleted file mode 100644
index 13835b7e2fa..00000000000
--- a/app/assets/javascripts/alert_management/components/alert_summary_row.vue
+++ /dev/null
@@ -1,18 +0,0 @@
-<script>
-export default {
- props: {
- label: {
- type: String,
- required: true,
- },
- },
-};
-</script>
-<template>
- <div class="gl-my-5 gl-display-flex">
- <div class="gl-font-weight-bold gl-w-13 gl-text-right gl-pr-3">{{ label }}</div>
- <div class="gl-pl-2">
- <slot></slot>
- </div>
- </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
deleted file mode 100644
index c39a72a45b9..00000000000
--- a/app/assets/javascripts/alert_management/components/sidebar/sidebar_assignee.vue
+++ /dev/null
@@ -1,38 +0,0 @@
-<script>
-import { GlDropdownItem } from '@gitlab/ui';
-
-export default {
- components: {
- GlDropdownItem,
- },
- props: {
- user: {
- type: Object,
- required: true,
- },
- active: {
- type: Boolean,
- required: true,
- },
- },
- methods: {
- isActive(name) {
- return this.alert.assignees.nodes.some(({ username }) => username === name);
- },
- },
-};
-</script>
-
-<template>
- <gl-dropdown-item
- :key="user.username"
- data-testid="assigneeDropdownItem"
- :active="active"
- active-class="is-active"
- :avatar-url="user.avatar_url"
- :secondary-text="`@${user.username}`"
- @click="$emit('update-alert-assignees', user.username)"
- >
- {{ user.name }}
- </gl-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
deleted file mode 100644
index 2a999b908f9..00000000000
--- a/app/assets/javascripts/alert_management/components/sidebar/sidebar_assignees.vue
+++ /dev/null
@@ -1,299 +0,0 @@
-<script>
-import {
- GlIcon,
- GlDropdown,
- GlDropdownDivider,
- GlDropdownSectionHeader,
- GlDropdownItem,
- GlSearchBoxByType,
- 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';
-
-const DATA_REFETCH_DELAY = 250;
-
-export default {
- i18n: {
- FETCH_USERS_ERROR: s__(
- 'AlertManagement|There was an error while updating the assignee(s) list. Please try again.',
- ),
- UPDATE_ALERT_ASSIGNEES_ERROR: s__(
- 'AlertManagement|There was an error while updating the assignee(s) of the alert. Please try again.',
- ),
- UPDATE_ALERT_ASSIGNEES_GRAPHQL_ERROR: s__(
- 'AlertManagement|This assignee cannot be assigned to this alert.',
- ),
- ASSIGNEES_BLOCK: s__('AlertManagement|Alert assignee(s): %{assignees}'),
- },
- components: {
- GlIcon,
- GlDropdown,
- GlDropdownItem,
- GlDropdownDivider,
- GlDropdownSectionHeader,
- GlSearchBoxByType,
- GlLoadingIcon,
- GlTooltip,
- GlButton,
- GlSprintf,
- SidebarAssignee,
- },
- props: {
- projectId: {
- type: String,
- required: true,
- },
- projectPath: {
- type: String,
- required: true,
- },
- alert: {
- type: Object,
- required: true,
- },
- isEditable: {
- type: Boolean,
- required: false,
- default: true,
- },
- sidebarCollapsed: {
- type: Boolean,
- required: false,
- },
- },
- data() {
- return {
- isDropdownShowing: false,
- isDropdownSearching: false,
- isUpdating: false,
- search: '',
- users: [],
- };
- },
- computed: {
- currentUser() {
- return gon?.current_username;
- },
- userName() {
- return this.alert?.assignees?.nodes[0]?.username;
- },
- userFullName() {
- return this.alert?.assignees?.nodes[0]?.name;
- },
- userImg() {
- return this.alert?.assignees?.nodes[0]?.avatarUrl;
- },
- sortedUsers() {
- return this.users
- .map((user) => ({ ...user, active: this.isActive(user.username) }))
- .sort((a, b) => (a.active === b.active ? 0 : a.active ? -1 : 1)); // eslint-disable-line no-nested-ternary
- },
- dropdownClass() {
- return this.isDropdownShowing ? 'dropdown-menu-selectable show' : 'gl-display-none';
- },
- dropDownTitle() {
- return this.userName ?? __('Select assignee');
- },
- userListValid() {
- return !this.isDropdownSearching && this.users.length > 0;
- },
- userListEmpty() {
- return !this.isDropdownSearching && this.users.length === 0;
- },
- },
- watch: {
- search: debounce(function debouncedUserSearch() {
- this.updateAssigneesDropdown();
- }, DATA_REFETCH_DELAY),
- },
- mounted() {
- this.updateAssigneesDropdown();
- },
- methods: {
- hideDropdown() {
- this.isDropdownShowing = false;
- },
- toggleFormDropdown() {
- this.isDropdownShowing = !this.isDropdownShowing;
- const { dropdown } = this.$refs.dropdown.$refs;
- if (dropdown && this.isDropdownShowing) {
- dropdown.show();
- }
- },
- isActive(name) {
- return this.alert.assignees.nodes.some(({ username }) => username === name);
- },
- buildUrl(urlRoot, url) {
- let newUrl;
- if (urlRoot != null) {
- newUrl = urlRoot.replace(/\/$/, '') + url;
- }
- return newUrl;
- },
- updateAssigneesDropdown() {
- this.isDropdownSearching = true;
- return axios
- .get(this.buildUrl(gon.relative_url_root, '/-/autocomplete/users.json'), {
- params: {
- search: this.search,
- per_page: 20,
- active: true,
- current_user: true,
- project_id: this.projectId,
- },
- })
- .then(({ data }) => {
- this.users = data;
- })
- .catch(() => {
- this.$emit('alert-error', this.$options.i18n.FETCH_USERS_ERROR);
- })
- .finally(() => {
- this.isDropdownSearching = false;
- });
- },
- updateAlertAssignees(assignees) {
- this.isUpdating = true;
- this.$apollo
- .mutate({
- mutation: alertSetAssignees,
- variables: {
- iid: this.alert.iid,
- assigneeUsernames: [this.isActive(assignees) ? '' : assignees],
- projectPath: this.projectPath,
- },
- })
- .then(({ data: { alertSetAssignees: { errors } = [] } = {} } = {}) => {
- this.hideDropdown();
-
- if (errors[0]) {
- this.$emit(
- 'alert-error',
- `${this.$options.i18n.UPDATE_ALERT_ASSIGNEES_GRAPHQL_ERROR} ${errors[0]}.`,
- );
- }
- })
- .catch(() => {
- this.$emit('alert-error', this.$options.i18n.UPDATE_ALERT_ASSIGNEES_ERROR);
- })
- .finally(() => {
- this.isUpdating = false;
- });
- },
- },
-};
-</script>
-
-<template>
- <div class="block alert-assignees">
- <div ref="assignees" class="sidebar-collapsed-icon" @click="$emit('toggle-sidebar')">
- <gl-icon name="user" :size="14" />
- <gl-loading-icon v-if="isUpdating" />
- </div>
- <gl-tooltip :target="() => $refs.assignees" boundary="viewport" placement="left">
- <gl-sprintf :message="$options.i18n.ASSIGNEES_BLOCK">
- <template #assignees>
- {{ userName }}
- </template>
- </gl-sprintf>
- </gl-tooltip>
-
- <div class="hide-collapsed">
- <p class="title gl-display-flex gl-justify-content-space-between">
- {{ __('Assignee') }}
- <a
- v-if="isEditable"
- ref="editButton"
- class="btn-link"
- href="#"
- @click="toggleFormDropdown"
- @keydown.esc="hideDropdown"
- >
- {{ __('Edit') }}
- </a>
- </p>
-
- <gl-dropdown
- ref="dropdown"
- :text="dropDownTitle"
- class="gl-w-full"
- :class="dropdownClass"
- toggle-class="dropdown-menu-toggle"
- @keydown.esc.native="hideDropdown"
- @hide="hideDropdown"
- >
- <p class="gl-new-dropdown-header-top">
- {{ __('Assign To') }}
- </p>
- <gl-search-box-by-type v-model.trim="search" :placeholder="__('Search users')" />
- <div class="dropdown-content dropdown-body">
- <template v-if="userListValid">
- <gl-dropdown-item
- :active="!userName"
- active-class="is-active"
- @click="updateAlertAssignees('')"
- >
- {{ __('Unassigned') }}
- </gl-dropdown-item>
- <gl-dropdown-divider />
-
- <gl-dropdown-section-header>
- {{ __('Assignee') }}
- </gl-dropdown-section-header>
- <sidebar-assignee
- v-for="user in sortedUsers"
- :key="user.username"
- :user="user"
- :active="user.active"
- @update-alert-assignees="updateAlertAssignees"
- />
- </template>
- <p v-else-if="userListEmpty" class="gl-mx-5 gl-my-4">
- {{ __('No Matching Results') }}
- </p>
- <gl-loading-icon v-else />
- </div>
- </gl-dropdown>
- </div>
-
- <gl-loading-icon v-if="isUpdating" :inline="true" />
- <div v-else-if="!isDropdownShowing" class="value gl-m-0" :class="{ 'no-value': !userName }">
- <div v-if="userName" class="gl-display-inline-flex gl-mt-2" data-testid="assigned-users">
- <span class="gl-relative gl-mr-4">
- <img
- :alt="userName"
- :src="userImg"
- :width="32"
- class="avatar avatar-inline gl-m-0 s32"
- data-qa-selector="avatar_image"
- />
- </span>
- <span class="gl-display-flex gl-flex-direction-column gl-overflow-hidden">
- <strong class="dropdown-menu-user-full-name">
- {{ userFullName }}
- </strong>
- <span class="dropdown-menu-user-username">@{{ userName }}</span>
- </span>
- </div>
- <span v-else class="gl-display-flex gl-align-items-center gl-line-height-normal">
- {{ __('None') }} -
- <gl-button
- class="gl-ml-2"
- href="#"
- variant="link"
- data-testid="unassigned-users"
- @click="updateAlertAssignees(currentUser)"
- >
- {{ __('assign yourself') }}
- </gl-button>
- </span>
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/alert_management/components/sidebar/sidebar_header.vue b/app/assets/javascripts/alert_management/components/sidebar/sidebar_header.vue
deleted file mode 100644
index 70902a204f8..00000000000
--- a/app/assets/javascripts/alert_management/components/sidebar/sidebar_header.vue
+++ /dev/null
@@ -1,41 +0,0 @@
-<script>
-import ToggleSidebar from '~/vue_shared/components/sidebar/toggle_sidebar.vue';
-import SidebarTodo from './sidebar_todo.vue';
-
-export default {
- components: {
- ToggleSidebar,
- SidebarTodo,
- },
- props: {
- alert: {
- type: Object,
- required: true,
- },
- projectPath: {
- type: String,
- required: true,
- },
- sidebarCollapsed: {
- type: Boolean,
- required: true,
- },
- },
-};
-</script>
-
-<template>
- <div class="block gl-display-flex gl-justify-content-space-between">
- <span class="issuable-header-text hide-collapsed">
- {{ __('To-Do') }}
- </span>
- <sidebar-todo
- v-if="!sidebarCollapsed"
- :project-path="projectPath"
- :alert="alert"
- :sidebar-collapsed="sidebarCollapsed"
- @alert-error="$emit('alert-error', $event)"
- />
- <toggle-sidebar :collapsed="sidebarCollapsed" @toggle="$emit('toggle-sidebar')" />
- </div>
-</template>
diff --git a/app/assets/javascripts/alert_management/components/sidebar/sidebar_status.vue b/app/assets/javascripts/alert_management/components/sidebar/sidebar_status.vue
deleted file mode 100644
index 0a2bad5510b..00000000000
--- a/app/assets/javascripts/alert_management/components/sidebar/sidebar_status.vue
+++ /dev/null
@@ -1,120 +0,0 @@
-<script>
-import { GlIcon, GlLoadingIcon, GlTooltip, GlSprintf } from '@gitlab/ui';
-import { s__ } from '~/locale';
-import AlertStatus from '../alert_status.vue';
-
-export default {
- statuses: {
- TRIGGERED: s__('AlertManagement|Triggered'),
- ACKNOWLEDGED: s__('AlertManagement|Acknowledged'),
- RESOLVED: s__('AlertManagement|Resolved'),
- },
- components: {
- GlIcon,
- GlLoadingIcon,
- GlTooltip,
- GlSprintf,
- AlertStatus,
- },
- props: {
- projectPath: {
- type: String,
- required: true,
- },
- alert: {
- type: Object,
- required: true,
- },
- isEditable: {
- type: Boolean,
- required: false,
- default: true,
- },
- },
- data() {
- return {
- isDropdownShowing: false,
- isUpdating: false,
- };
- },
- computed: {
- dropdownClass() {
- return this.isDropdownShowing ? 'show' : 'gl-display-none';
- },
- },
- methods: {
- hideDropdown() {
- this.isDropdownShowing = false;
- },
- toggleFormDropdown() {
- this.isDropdownShowing = !this.isDropdownShowing;
- const { dropdown } = this.$children[2].$refs.dropdown.$refs;
- if (dropdown && this.isDropdownShowing) {
- dropdown.show();
- }
- },
- handleUpdating(updating) {
- this.isUpdating = updating;
- },
- },
-};
-</script>
-
-<template>
- <div class="block alert-status">
- <div ref="status" class="sidebar-collapsed-icon" @click="$emit('toggle-sidebar')">
- <gl-icon name="status" :size="14" />
- <gl-loading-icon v-if="isUpdating" />
- </div>
- <gl-tooltip :target="() => $refs.status" boundary="viewport" placement="left">
- <gl-sprintf :message="s__('AlertManagement|Alert status: %{status}')">
- <template #status>
- {{ alert.status.toLowerCase() }}
- </template>
- </gl-sprintf>
- </gl-tooltip>
-
- <div class="hide-collapsed">
- <p class="title gl-display-flex justify-content-between">
- {{ s__('AlertManagement|Status') }}
- <a
- v-if="isEditable"
- ref="editButton"
- class="btn-link"
- href="#"
- @click="toggleFormDropdown"
- @keydown.esc="hideDropdown"
- >
- {{ s__('AlertManagement|Edit') }}
- </a>
- </p>
-
- <alert-status
- :alert="alert"
- :project-path="projectPath"
- :is-dropdown-showing="isDropdownShowing"
- :is-sidebar="true"
- @alert-error="$emit('alert-error', $event)"
- @hide-dropdown="hideDropdown"
- @handle-updating="handleUpdating"
- />
-
- <gl-loading-icon v-if="isUpdating" :inline="true" />
- <p
- v-else-if="!isDropdownShowing"
- class="value gl-m-0"
- :class="{ 'no-value': !$options.statuses[alert.status] }"
- >
- <span
- v-if="$options.statuses[alert.status]"
- class="gl-text-gray-500"
- data-testid="status"
- >{{ $options.statuses[alert.status] }}</span
- >
- <span v-else>
- {{ s__('AlertManagement|None') }}
- </span>
- </p>
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/alert_management/components/sidebar/sidebar_todo.vue b/app/assets/javascripts/alert_management/components/sidebar/sidebar_todo.vue
deleted file mode 100644
index 485395bcac2..00000000000
--- a/app/assets/javascripts/alert_management/components/sidebar/sidebar_todo.vue
+++ /dev/null
@@ -1,149 +0,0 @@
-<script>
-import produce from 'immer';
-import { s__ } from '~/locale';
-import Todo from '~/sidebar/components/todo_toggle/todo.vue';
-import createAlertTodoMutation from '../../graphql/mutations/alert_todo_create.mutation.graphql';
-import todoMarkDoneMutation from '~/graphql_shared/mutations/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.',
- ),
- },
- components: {
- Todo,
- },
- props: {
- alert: {
- type: Object,
- required: true,
- },
- projectPath: {
- type: String,
- required: true,
- },
- sidebarCollapsed: {
- type: Boolean,
- required: true,
- },
- },
- data() {
- return {
- isUpdating: false,
- };
- },
- 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) {
- const oldCount = parseInt(document.querySelector('.js-todos-count').innerText, 10);
- const count = add ? oldCount + 1 : oldCount - 1;
- const headerTodoEvent = new CustomEvent('todo:toggle', {
- detail: {
- count,
- },
- });
-
- return document.dispatchEvent(headerTodoEvent);
- },
- addToDo() {
- this.isUpdating = true;
- return this.$apollo
- .mutate({
- mutation: createAlertTodoMutation,
- variables: {
- iid: this.alert.iid,
- projectPath: this.projectPath,
- },
- })
- .then(({ data: { errors = [] } }) => {
- if (errors[0]) {
- return this.throwError(errors[0]);
- }
- return this.updateToDoCount(true);
- })
- .catch(() => {
- this.throwError();
- })
- .finally(() => {
- this.isUpdating = false;
- });
- },
- markAsDone() {
- this.isUpdating = true;
- return this.$apollo
- .mutate({
- mutation: todoMarkDoneMutation,
- variables: {
- id: this.firstToDoId,
- },
- update: this.updateCache,
- })
- .then(({ data: { errors = [] } }) => {
- if (errors[0]) {
- return this.throwError(errors[0]);
- }
- return this.updateToDoCount(false);
- })
- .catch(() => {
- this.throwError();
- })
- .finally(() => {
- this.isUpdating = false;
- });
- },
- updateCache(store) {
- const sourceData = store.readQuery({
- query: alertQuery,
- variables: this.getAlertQueryVariables,
- });
-
- const data = produce(sourceData, (draftData) => {
- // eslint-disable-next-line no-param-reassign
- draftData.project.alertManagementAlerts.nodes[0].todos.nodes = [];
- });
-
- 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>
-
-<template>
- <div :class="{ 'block todo': sidebarCollapsed, 'gl-ml-auto': !sidebarCollapsed }">
- <todo
- data-testid="alert-todo-button"
- :collapsed="sidebarCollapsed"
- :issuable-id="alertID"
- :is-todo="hasPendingTodos"
- :is-action-active="isUpdating"
- issuable-type="alert"
- @toggleTodo="hasPendingTodos ? markAsDone() : addToDo()"
- />
- </div>
-</template>
diff --git a/app/assets/javascripts/alert_management/components/system_notes/system_note.vue b/app/assets/javascripts/alert_management/components/system_notes/system_note.vue
deleted file mode 100644
index 3705e36a579..00000000000
--- a/app/assets/javascripts/alert_management/components/system_notes/system_note.vue
+++ /dev/null
@@ -1,48 +0,0 @@
-<script>
-/* eslint-disable vue/no-v-html */
-import { GlIcon } from '@gitlab/ui';
-import NoteHeader from '~/notes/components/note_header.vue';
-
-export default {
- components: {
- NoteHeader,
- GlIcon,
- },
- props: {
- note: {
- type: Object,
- required: true,
- },
- },
- computed: {
- noteAnchorId() {
- return `note_${this.note?.id?.split('/').pop()}`;
- },
- noteAuthor() {
- const {
- author,
- author: { id },
- } = this.note;
- return { ...author, id: id?.split('/').pop() };
- },
- },
-};
-</script>
-
-<template>
- <li :id="noteAnchorId" class="timeline-entry note system-note note-wrapper gl-p-0!">
- <div class="gl-display-inline-flex gl-align-items-center">
- <div
- class="gl-display-inline gl-bg-white gl-text-gray-200 gl-border-gray-100 gl-border-1 gl-border-solid gl-rounded-full gl-box-sizing-content-box gl-p-3 gl-mt-n2 gl-mr-6"
- >
- <gl-icon :name="note.systemNoteIconName" />
- </div>
-
- <div class="note-header">
- <note-header :author="noteAuthor" :created-at="note.createdAt" :note-id="note.id">
- <span v-html="note.bodyHtml"></span>
- </note-header>
- </div>
- </div>
- </li>
-</template>