import {css} from '@emotion/react'; import styled from '@emotion/styled'; import moment from 'moment-timezone'; import CollapsePanel from 'sentry/components/collapsePanel'; import DateTime from 'sentry/components/dateTime'; import Duration from 'sentry/components/duration'; import ErrorBoundary from 'sentry/components/errorBoundary'; import Link from 'sentry/components/links/link'; import PanelTable from 'sentry/components/panels/panelTable'; import StatusIndicator from 'sentry/components/statusIndicator'; import {t, tct, tn} from 'sentry/locale'; import space from 'sentry/styles/space'; import {Organization} from 'sentry/types'; import getDynamicText from 'sentry/utils/getDynamicText'; import {AlertRuleThresholdType} from 'sentry/views/alerts/rules/metric/types'; import {Incident, IncidentActivityType, IncidentStatus} from 'sentry/views/alerts/types'; import {alertDetailsLink} from 'sentry/views/alerts/utils'; import {AlertWizardAlertNames} from 'sentry/views/alerts/wizard/options'; import {getAlertTypeFromAggregateDataset} from 'sentry/views/alerts/wizard/utils'; const COLLAPSE_COUNT = 3; function getTriggerName(value: string | null) { if (value === `${IncidentStatus.WARNING}`) { return t('Warning'); } if (value === `${IncidentStatus.CRITICAL}`) { return t('Critical'); } // Otherwise, activity type is not status change return ''; } type MetricAlertActivityProps = { incident: Incident; organization: Organization; }; function MetricAlertActivity({organization, incident}: MetricAlertActivityProps) { const activities = (incident.activities ?? []).filter( activity => activity.type === IncidentActivityType.STATUS_CHANGE ); const criticalActivity = activities.filter( activity => activity.value === `${IncidentStatus.CRITICAL}` ); const warningActivity = activities.filter( activity => activity.value === `${IncidentStatus.WARNING}` ); const triggeredActivity = criticalActivity.length ? criticalActivity[0] : warningActivity[0]; const currentTrigger = getTriggerName(triggeredActivity.value); const nextActivity = activities.find( ({previousValue}) => previousValue === triggeredActivity.value ); const activityDuration = ( nextActivity ? moment(nextActivity.dateCreated) : moment() ).diff(moment(triggeredActivity.dateCreated), 'milliseconds'); const threshold = activityDuration !== null && tct('[duration]', { duration: , }); const warningThreshold = incident.alertRule.triggers .filter(trigger => trigger.label === 'warning') .map(trig => trig.alertThreshold); const criticalThreshold = incident.alertRule.triggers .filter(trigger => trigger.label === 'critical') .map(trig => trig.alertThreshold); return ( <StatusIndicator status={currentTrigger.toLocaleLowerCase()} tooltipTitle={tct('Status: [level]', {level: currentTrigger})} /> <Link to={{ pathname: alertDetailsLink(organization, incident), query: {alert: incident.identifier}, }} > {tct('#[id]', {id: incident.identifier})} </Link> {tct('[title] [selector] [threshold]', { title: AlertWizardAlertNames[getAlertTypeFromAggregateDataset(incident.alertRule)], selector: incident.alertRule.thresholdType === AlertRuleThresholdType.ABOVE ? 'above' : 'below', threshold: currentTrigger === 'Warning' ? warningThreshold : criticalThreshold, })} {getDynamicText({ value: threshold, fixed: '30s', })} ); } type Props = { organization: Organization; incidents?: Incident[]; }; function MetricHistory({organization, incidents}: Props) { const numOfIncidents = (incidents ?? []).length; return ( {({isExpanded, showMoreButton}) => (
{incidents && incidents.map((incident, idx) => { if (idx >= COLLAPSE_COUNT && !isExpanded) { return null; } return ( ); })} {showMoreButton}
)}
); } export default MetricHistory; const StyledPanelTable = styled(PanelTable)<{expanded: boolean; isEmpty: boolean}>` grid-template-columns: max-content 1fr repeat(2, max-content); & > div { padding: ${space(1)} ${space(2)}; } div:last-of-type { padding: ${p => p.isEmpty && `48px ${space(1)}`}; } ${p => !p.expanded && css` margin-bottom: 0px; border-bottom-left-radius: 0px; border-bottom-right-radius: 0px; border-bottom: none; `} `; const StyledDateTime = styled(DateTime)` color: ${p => p.theme.gray300}; font-size: ${p => p.theme.fontSizeMedium}; display: flex; justify-content: flex-start; padding: ${space(1)} ${space(2)} !important; `; const Title = styled('div')` display: flex; align-items: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; width: 100%; font-size: ${p => p.theme.fontSizeMedium}; padding: ${space(1)}; `; const Cell = styled('div')` display: flex; align-items: center; white-space: nowrap; font-size: ${p => p.theme.fontSizeMedium}; padding: ${space(1)}; `;