From 1ece740030719f934c8be563c74e034a4c528834 Mon Sep 17 00:00:00 2001 From: Maxim Mordasov Date: Fri, 2 Feb 2024 10:56:27 +0300 Subject: [PATCH] Render alert group action buttons even if getting AG data fails (#3746) # What this PR does Render alert group action buttons even if getting AG data fails ## Which issue(s) this PR fixes https://github.com/grafana/oncall-private/issues/2383 ## Checklist - [ ] Unit, integration, and e2e (if applicable) tests updated - [ ] Documentation added (or `pr:no public docs` PR label added if not required) - [ ] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not required) --- CHANGELOG.md | 1 + grafana-plugin/src/assets/img/error.svg | 32 ++++++++ .../PageErrorHandlingWrapper.helpers.tsx | 4 +- .../PageErrorHandlingWrapper.tsx | 1 + .../src/pages/incident/Incident.helpers.tsx | 54 ++++++++------ .../src/pages/incident/Incident.module.scss | 10 +++ .../src/pages/incident/Incident.tsx | 74 ++++++++++++++----- .../src/pages/incidents/Incidents.module.scss | 6 -- 8 files changed, 131 insertions(+), 51 deletions(-) create mode 100644 grafana-plugin/src/assets/img/error.svg diff --git a/CHANGELOG.md b/CHANGELOG.md index cce96c31..eec810b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Render alert group action buttons even if getting AG data fails ([#2383](https://github.com/grafana/oncall-private/issues/2383)) - Enable Grafana Alerting V2 feature flag by default ## v1.3.98 (2024-02-01) diff --git a/grafana-plugin/src/assets/img/error.svg b/grafana-plugin/src/assets/img/error.svg new file mode 100644 index 00000000..36d139b6 --- /dev/null +++ b/grafana-plugin/src/assets/img/error.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/grafana-plugin/src/components/PageErrorHandlingWrapper/PageErrorHandlingWrapper.helpers.tsx b/grafana-plugin/src/components/PageErrorHandlingWrapper/PageErrorHandlingWrapper.helpers.tsx index 840e0945..7653be75 100644 --- a/grafana-plugin/src/components/PageErrorHandlingWrapper/PageErrorHandlingWrapper.helpers.tsx +++ b/grafana-plugin/src/components/PageErrorHandlingWrapper/PageErrorHandlingWrapper.helpers.tsx @@ -1,7 +1,7 @@ import { PageErrorData } from 'components/PageErrorHandlingWrapper/PageErrorHandlingWrapper'; export function initErrorDataState(): Partial { - return { isWrongTeamError: false, wrongTeamNoPermissions: false }; + return { isUnknownError: false, isWrongTeamError: false, wrongTeamNoPermissions: false }; } export function getWrongTeamResponseInfo({ response }): Partial { @@ -18,5 +18,5 @@ export function getWrongTeamResponseInfo({ response }): Partial { } } - return { isNotFoundError: true }; + return { isUnknownError: true }; } diff --git a/grafana-plugin/src/components/PageErrorHandlingWrapper/PageErrorHandlingWrapper.tsx b/grafana-plugin/src/components/PageErrorHandlingWrapper/PageErrorHandlingWrapper.tsx index 6507b65a..4d3a64fe 100644 --- a/grafana-plugin/src/components/PageErrorHandlingWrapper/PageErrorHandlingWrapper.tsx +++ b/grafana-plugin/src/components/PageErrorHandlingWrapper/PageErrorHandlingWrapper.tsx @@ -18,6 +18,7 @@ export interface PageBaseState { export interface PageErrorData { isNotFoundError?: boolean; isWrongTeamError?: boolean; + isUnknownError?: boolean; wrongTeamNoPermissions?: boolean; switchToTeam?: { name: string; id: string }; } diff --git a/grafana-plugin/src/pages/incident/Incident.helpers.tsx b/grafana-plugin/src/pages/incident/Incident.helpers.tsx index 938b4841..6f253a56 100644 --- a/grafana-plugin/src/pages/incident/Incident.helpers.tsx +++ b/grafana-plugin/src/pages/incident/Incident.helpers.tsx @@ -144,16 +144,16 @@ export function renderRelatedUsers(incident: Alert, isFull = false) { ); } -export function getActionButtons(incident: AlertType, cx: any, callbacks: { [key: string]: any }) { - if (incident.root_alert_group) { +export function getActionButtons(incident: AlertType, callbacks: { [key: string]: any }, allSecondary = false) { + const { onResolve, onUnresolve, onAcknowledge, onUnacknowledge, onSilence, onUnsilence } = callbacks; + + if (incident?.root_alert_group) { return null; } - const { onResolve, onUnresolve, onAcknowledge, onUnacknowledge, onSilence, onUnsilence } = callbacks; - const resolveButton = ( - @@ -161,15 +161,15 @@ export function getActionButtons(incident: AlertType, cx: any, callbacks: { [key const unacknowledgeButton = ( - ); const unresolveButton = ( - - @@ -177,31 +177,37 @@ export function getActionButtons(incident: AlertType, cx: any, callbacks: { [key const acknowledgeButton = ( - ); + const silenceButton = ( + + + + ); + + const unsilenceButton = ( + + + + ); + + if (incident?.status === undefined) { + // to render all buttons if status unknown + return [acknowledgeButton, unacknowledgeButton, resolveButton, unresolveButton, silenceButton, unsilenceButton]; + } + const buttons = []; if (incident.status === IncidentStatus.Silenced) { - buttons.push( - - - - ); + buttons.push(unsilenceButton); } else if (incident.status !== IncidentStatus.Resolved) { - buttons.push( - - ); + buttons.push(silenceButton); } if (!incident.resolved && !incident.acknowledged) { diff --git a/grafana-plugin/src/pages/incident/Incident.module.scss b/grafana-plugin/src/pages/incident/Incident.module.scss index d3a6fd04..b578ec91 100644 --- a/grafana-plugin/src/pages/incident/Incident.module.scss +++ b/grafana-plugin/src/pages/incident/Incident.module.scss @@ -92,6 +92,16 @@ text-align: center; } +.alert-group-stub { + margin: 24px auto; + width: 520px; + text-align: center; +} + +.alert-group-stub-divider { + width: 520px; +} + .timeline-icon-background { width: 28px; height: 28px; diff --git a/grafana-plugin/src/pages/incident/Incident.tsx b/grafana-plugin/src/pages/incident/Incident.tsx index 852627b2..9d0909ce 100644 --- a/grafana-plugin/src/pages/incident/Incident.tsx +++ b/grafana-plugin/src/pages/incident/Incident.tsx @@ -14,6 +14,7 @@ import { Field, Modal, Tooltip, + Divider, } from '@grafana/ui'; import cn from 'classnames/bind'; import { observer } from 'mobx-react'; @@ -24,6 +25,7 @@ import { RouteComponentProps, withRouter } from 'react-router-dom'; import reactStringReplace from 'react-string-replace'; import { OnCallPluginExtensionPoints } from 'types'; +import errorSVG from 'assets/img/error.svg'; import Collapse from 'components/Collapse/Collapse'; import { ExtensionLinkDropdown } from 'components/ExtensionLinkMenu/ExtensionLinkDropdown'; import Block from 'components/GBlock/Block'; @@ -43,14 +45,7 @@ import { prepareForUpdate } from 'containers/AddResponders/AddResponders.helpers import { UserResponder } from 'containers/AddResponders/AddResponders.types'; import AttachIncidentForm from 'containers/AttachIncidentForm/AttachIncidentForm'; import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip'; -import { - Alert as AlertType, - Alert, - AlertAction, - TimeLineItem, - TimeLineRealm, - GroupedAlert, -} from 'models/alertgroup/alertgroup.types'; +import { Alert, AlertAction, TimeLineItem, TimeLineRealm, GroupedAlert } from 'models/alertgroup/alertgroup.types'; import { ResolutionNoteSourceTypesToDisplayName } from 'models/resolution_note/resolution_note.types'; import { User } from 'models/user/user.types'; import { IncidentDropdown } from 'pages/incidents/parts/IncidentDropdown'; @@ -133,12 +128,31 @@ class IncidentPage extends React.Component } = this.props; const { errorData, showIntegrationSettings, showAttachIncidentForm } = this.state; - const { isNotFoundError, isWrongTeamError } = errorData; + const { isNotFoundError, isWrongTeamError, isUnknownError } = errorData; // const { alertReceiveChannelStore } = store; const { alerts } = store.alertGroupStore; const incident = alerts.get(id); + if (isUnknownError) { + return ( + + ); + } + if (!incident && !isNotFoundError && !isWrongTeamError) { return (
@@ -332,8 +346,8 @@ class IncidentPage extends React.Component onUnacknowledge={this.getOnActionButtonClick(incident.pk, AlertAction.unAcknowledge)} onUnresolve={this.getOnActionButtonClick(incident.pk, AlertAction.unResolve)} onAcknowledge={this.getOnActionButtonClick(incident.pk, AlertAction.Acknowledge)} - onSilence={this.getSilenceClickHandler(incident)} - onUnsilence={this.getUnsilenceClickHandler(incident)} + onSilence={this.getSilenceClickHandler(incident.pk)} + onUnsilence={this.getUnsilenceClickHandler(incident.pk)} />
@@ -413,13 +427,13 @@ class IncidentPage extends React.Component - {getActionButtons(incident, cx, { + {getActionButtons(incident, { onResolve: this.getOnActionButtonClick(incident.pk, AlertAction.Resolve), onUnacknowledge: this.getOnActionButtonClick(incident.pk, AlertAction.unAcknowledge), onUnresolve: this.getOnActionButtonClick(incident.pk, AlertAction.unResolve), onAcknowledge: this.getOnActionButtonClick(incident.pk, AlertAction.Acknowledge), - onSilence: this.getSilenceClickHandler(incident), - onUnsilence: this.getUnsilenceClickHandler(incident), + onSilence: this.getSilenceClickHandler(incident.pk), + onUnsilence: this.getUnsilenceClickHandler(incident.pk), })} {incident.grafana_incident_id === null && ( @@ -615,7 +629,7 @@ class IncidentPage extends React.Component }; }; - getOnActionButtonClick = (incidentId: string, action: AlertAction) => { + getOnActionButtonClick = (incidentId: Alert['pk'], action: AlertAction) => { const { store } = this.props; return (e: SyntheticEvent) => { @@ -625,23 +639,23 @@ class IncidentPage extends React.Component }; }; - getSilenceClickHandler = (alert: AlertType) => { + getSilenceClickHandler = (incidentId: Alert['pk']) => { const { store } = this.props; return (value: number) => { - return store.alertGroupStore.doIncidentAction(alert.pk, AlertAction.Silence, false, { + return store.alertGroupStore.doIncidentAction(incidentId, AlertAction.Silence, false, { delay: value, }); }; }; - getUnsilenceClickHandler = (alert: AlertType) => { + getUnsilenceClickHandler = (incidentId: Alert['pk']) => { const { store } = this.props; return (event: any) => { event.stopPropagation(); - return store.alertGroupStore.doIncidentAction(alert.pk, AlertAction.unSilence, false); + return store.alertGroupStore.doIncidentAction(incidentId, AlertAction.unSilence, false); }; }; @@ -825,4 +839,26 @@ function AttachedIncidentsList({ ); } +const AlertGroupStub = ({ buttons }: { buttons: React.ReactNode }) => { + return ( +
+ + + An unexpected error happened + + OnCall is not able to receive any information about the current Alert Group. It's unknown if it's firing, + acknowledged, silenced, or resolved. + +
+ +
+ Meanwhile, you could try changing the status of this Alert Group: + + {buttons} + +
+
+ ); +}; + export default withRouter(withMobXProviderContext(IncidentPage)); diff --git a/grafana-plugin/src/pages/incidents/Incidents.module.scss b/grafana-plugin/src/pages/incidents/Incidents.module.scss index 908029d6..b73e8560 100644 --- a/grafana-plugin/src/pages/incidents/Incidents.module.scss +++ b/grafana-plugin/src/pages/incidents/Incidents.module.scss @@ -45,12 +45,6 @@ color: var(--secondary-text-color); } -.silence-button-inline { - font-size: 12px; - height: 24px; - margin-right: 0; -} - .pagination { width: 100%; margin-top: 20px;