activatedMetricAlertRuleStatus.tsx 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import type {ReactNode} from 'react';
  2. import styled from '@emotion/styled';
  3. import {IconArrow, IconMute} from 'sentry/icons';
  4. import {t} from 'sentry/locale';
  5. import {space} from 'sentry/styles/space';
  6. import type {ColorOrAlias} from 'sentry/utils/theme';
  7. import {hasActiveIncident} from 'sentry/views/alerts/list/rules/utils';
  8. import {getThresholdUnits} from 'sentry/views/alerts/rules/metric/constants';
  9. import {
  10. AlertRuleComparisonType,
  11. AlertRuleThresholdType,
  12. AlertRuleTriggerType,
  13. } from 'sentry/views/alerts/rules/metric/types';
  14. import type {MetricAlert} from '../../types';
  15. import {IncidentStatus} from '../../types';
  16. interface Props {
  17. rule: MetricAlert;
  18. }
  19. export default function ActivatedMetricAlertRuleStatus({rule}: Props): ReactNode {
  20. if (rule.snooze) {
  21. return (
  22. <IssueAlertStatusWrapper>
  23. <IconMute size="sm" color="subText" />
  24. {t('Muted')}
  25. </IssueAlertStatusWrapper>
  26. );
  27. }
  28. const isUnhealthy = hasActiveIncident(rule);
  29. let iconColor: ColorOrAlias = 'successText';
  30. let iconDirection: 'up' | 'down' =
  31. rule.thresholdType === AlertRuleThresholdType.ABOVE ? 'down' : 'up';
  32. let thresholdTypeText =
  33. rule.thresholdType === AlertRuleThresholdType.ABOVE ? t('Below') : t('Above');
  34. if (isUnhealthy) {
  35. iconColor =
  36. rule.latestIncident?.status === IncidentStatus.CRITICAL
  37. ? 'errorText'
  38. : 'warningText';
  39. // if unhealthy, swap icon direction
  40. iconDirection = rule.thresholdType === AlertRuleThresholdType.ABOVE ? 'up' : 'down';
  41. thresholdTypeText =
  42. rule.thresholdType === AlertRuleThresholdType.ABOVE ? t('Above') : t('Below');
  43. }
  44. let threshold = rule.triggers.find(
  45. ({label}) => label === AlertRuleTriggerType.CRITICAL
  46. )?.alertThreshold;
  47. if (isUnhealthy && rule.latestIncident?.status === IncidentStatus.WARNING) {
  48. threshold = rule.triggers.find(
  49. ({label}) => label === AlertRuleTriggerType.WARNING
  50. )?.alertThreshold;
  51. } else if (!isUnhealthy && rule.latestIncident && rule.resolveThreshold) {
  52. threshold = rule.resolveThreshold;
  53. }
  54. return (
  55. <FlexCenter>
  56. <IconArrow color={iconColor} direction={iconDirection} />
  57. <TriggerText>
  58. {`${thresholdTypeText} ${threshold}`}
  59. {getThresholdUnits(
  60. rule.aggregate,
  61. rule.comparisonDelta
  62. ? AlertRuleComparisonType.CHANGE
  63. : AlertRuleComparisonType.COUNT
  64. )}
  65. </TriggerText>
  66. </FlexCenter>
  67. );
  68. }
  69. // TODO: see static/app/components/profiling/flex.tsx and utilize the FlexContainer styled component
  70. const FlexCenter = styled('div')`
  71. display: flex;
  72. flex-direction: row;
  73. align-items: center;
  74. `;
  75. const IssueAlertStatusWrapper = styled('div')`
  76. display: flex;
  77. align-items: center;
  78. gap: ${space(1)};
  79. line-height: 2;
  80. `;
  81. const TriggerText = styled('div')`
  82. margin-left: ${space(1)};
  83. white-space: nowrap;
  84. font-variant-numeric: tabular-nums;
  85. `;