utils.tsx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. import {Fragment, useMemo} from 'react';
  2. import {useTheme} from '@emotion/react';
  3. import moment from 'moment-timezone';
  4. import Legend from 'sentry/components/charts/components/legend';
  5. import {usePageFilterDates} from 'sentry/components/checkInTimeline/hooks/useMonitorDates';
  6. import {DateTime} from 'sentry/components/dateTime';
  7. import {t} from 'sentry/locale';
  8. import type {Series} from 'sentry/types/echarts';
  9. import type {Event} from 'sentry/types/event';
  10. import type {GroupOpenPeriod} from 'sentry/types/group';
  11. import {useApiQuery} from 'sentry/utils/queryClient';
  12. import useOrganization from 'sentry/utils/useOrganization';
  13. import {useUser} from 'sentry/utils/useUser';
  14. import type {TimePeriodType} from 'sentry/views/alerts/rules/metric/details/constants';
  15. import {type MetricRule, TimePeriod} from 'sentry/views/alerts/rules/metric/types';
  16. import {useIssueDetails} from 'sentry/views/issueDetails/streamline/context';
  17. import {getGroupEventQueryKey} from 'sentry/views/issueDetails/utils';
  18. export function useMetricIssueAlertId({groupId}: {groupId: string}): string | undefined {
  19. /**
  20. * This should be removed once the metric alert rule ID is set on the issue.
  21. * This will fetch an event from the max range if the detector details
  22. * are not available (e.g. time range has changed and page refreshed)
  23. */
  24. const user = useUser();
  25. const organization = useOrganization();
  26. const {detectorDetails} = useIssueDetails();
  27. const {detectorId, detectorType} = detectorDetails;
  28. const hasMetricDetector = detectorId && detectorType === 'metric_alert';
  29. const {data: event} = useApiQuery<Event>(
  30. getGroupEventQueryKey({
  31. orgSlug: organization.slug,
  32. groupId,
  33. eventId: user.options.defaultIssueEvent,
  34. environments: [],
  35. }),
  36. {
  37. staleTime: Infinity,
  38. enabled: !hasMetricDetector,
  39. retry: false,
  40. }
  41. );
  42. // Fall back to the fetched event in case the provider doesn't have the detector details
  43. return hasMetricDetector ? detectorId : event?.contexts?.metric_alert?.alert_rule_id;
  44. }
  45. interface UseMetricTimePeriodParams {
  46. openPeriod?: GroupOpenPeriod;
  47. }
  48. export function useMetricTimePeriod({
  49. openPeriod,
  50. }: UseMetricTimePeriodParams = {}): TimePeriodType {
  51. const {since, until} = usePageFilterDates();
  52. return useMemo(() => {
  53. const start = openPeriod?.start ?? since.toISOString();
  54. let end = openPeriod?.end ?? until.toISOString();
  55. if (!end) {
  56. end = new Date().toISOString();
  57. }
  58. return {
  59. start,
  60. end,
  61. period: TimePeriod.SEVEN_DAYS,
  62. usingPeriod: false,
  63. label: t('Custom time'),
  64. display: (
  65. <Fragment>
  66. <DateTime date={moment.utc(start)} />
  67. {' — '}
  68. <DateTime date={moment.utc(end)} />
  69. </Fragment>
  70. ),
  71. custom: true,
  72. };
  73. }, [openPeriod, since, until]);
  74. }
  75. export function useMetricIssueLegend({
  76. rule,
  77. series,
  78. }: {
  79. rule: MetricRule;
  80. series: Series[];
  81. }) {
  82. const theme = useTheme();
  83. return useMemo(() => {
  84. const legendSet = new Set(series.map(s => s.seriesName));
  85. if (legendSet.has(rule.aggregate)) {
  86. legendSet.delete(rule.aggregate);
  87. }
  88. const legendItems = Array.from(legendSet).map(name => {
  89. switch (name) {
  90. case 'Threshold Line':
  91. return {
  92. name,
  93. icon: HORIZONTAL_DASHED_LINE_CHART_ICON,
  94. itemStyle: {
  95. color: theme.error,
  96. },
  97. };
  98. case 'Incident Line':
  99. return {
  100. name,
  101. icon: VERTICAL_LINE_CHART_ICON,
  102. itemStyle: {
  103. color: theme.error,
  104. },
  105. };
  106. case 'Status Area':
  107. return {
  108. name,
  109. icon: HORIZONTAL_BAR_CHART_ICON,
  110. itemStyle: {
  111. color: theme.error,
  112. },
  113. };
  114. default:
  115. return {
  116. name,
  117. icon: 'circle',
  118. color: theme.gray300,
  119. };
  120. }
  121. });
  122. return Legend({
  123. theme,
  124. orient: 'horizontal',
  125. align: 'left',
  126. show: true,
  127. data: legendItems,
  128. inactiveColor: theme.gray200,
  129. });
  130. }, [rule, series, theme]);
  131. }
  132. /**
  133. * The legend icon for the dashed horizontal lines in a chart, commonly for thresholds.
  134. */
  135. const HORIZONTAL_DASHED_LINE_CHART_ICON =
  136. 'path://M180 1000 l0 -80 200 0 200 0 0 80 0 80 -200 0 -200 0 0 -80z, M810 1000 l0 -80 200 0 200 0 0 80 0 80 -200 0 -200 0 0 -80zm, M1440 1000 l0 -80 200 0 200 0 0 80 0 80 -200 0 -200 0 0 -80z';
  137. /**
  138. * The legend icon for the vertical lines in a chart, commonly for incidents, or releases.
  139. */
  140. const VERTICAL_LINE_CHART_ICON =
  141. 'path://M1000 180 l0 -40 40 0 40 0 0 200 0 200 0 200 0 200 -40 0 -40 0 0 -200 0 -200 0 -200 0 -200z';
  142. /**
  143. * The legend icon for the thick horizontal bar along y=0 in a chart, commonly for status areas in metric alerts.
  144. */
  145. const HORIZONTAL_BAR_CHART_ICON =
  146. 'path://M180 1000 l0 -160 400 0 400 0 0 160 0 160 -400 0 -400 0 0 -160z';