import {Fragment, useMemo} from 'react'; import {useTheme} from '@emotion/react'; import moment from 'moment-timezone'; import Legend from 'sentry/components/charts/components/legend'; import {usePageFilterDates} from 'sentry/components/checkInTimeline/hooks/useMonitorDates'; import {DateTime} from 'sentry/components/dateTime'; import {t} from 'sentry/locale'; import type {Series} from 'sentry/types/echarts'; import type {Event} from 'sentry/types/event'; import type {GroupOpenPeriod} from 'sentry/types/group'; import {useApiQuery} from 'sentry/utils/queryClient'; import useOrganization from 'sentry/utils/useOrganization'; import {useUser} from 'sentry/utils/useUser'; import type {TimePeriodType} from 'sentry/views/alerts/rules/metric/details/constants'; import {type MetricRule, TimePeriod} from 'sentry/views/alerts/rules/metric/types'; import {useIssueDetails} from 'sentry/views/issueDetails/streamline/context'; import {getGroupEventQueryKey} from 'sentry/views/issueDetails/utils'; export function useMetricIssueAlertId({groupId}: {groupId: string}): string | undefined { /** * This should be removed once the metric alert rule ID is set on the issue. * This will fetch an event from the max range if the detector details * are not available (e.g. time range has changed and page refreshed) */ const user = useUser(); const organization = useOrganization(); const {detectorDetails} = useIssueDetails(); const {detectorId, detectorType} = detectorDetails; const hasMetricDetector = detectorId && detectorType === 'metric_alert'; const {data: event} = useApiQuery( getGroupEventQueryKey({ orgSlug: organization.slug, groupId, eventId: user.options.defaultIssueEvent, environments: [], }), { staleTime: Infinity, enabled: !hasMetricDetector, retry: false, } ); // Fall back to the fetched event in case the provider doesn't have the detector details return hasMetricDetector ? detectorId : event?.contexts?.metric_alert?.alert_rule_id; } interface UseMetricTimePeriodParams { openPeriod?: GroupOpenPeriod; } export function useMetricTimePeriod({ openPeriod, }: UseMetricTimePeriodParams = {}): TimePeriodType { const {since, until} = usePageFilterDates(); return useMemo(() => { const start = openPeriod?.start ?? since.toISOString(); let end = openPeriod?.end ?? until.toISOString(); if (!end) { end = new Date().toISOString(); } return { start, end, period: TimePeriod.SEVEN_DAYS, usingPeriod: false, label: t('Custom time'), display: ( {' — '} ), custom: true, }; }, [openPeriod, since, until]); } export function useMetricIssueLegend({ rule, series, }: { rule: MetricRule; series: Series[]; }) { const theme = useTheme(); return useMemo(() => { const legendSet = new Set(series.map(s => s.seriesName)); if (legendSet.has(rule.aggregate)) { legendSet.delete(rule.aggregate); } const legendItems = Array.from(legendSet).map(name => { switch (name) { case 'Threshold Line': return { name, icon: HORIZONTAL_DASHED_LINE_CHART_ICON, itemStyle: { color: theme.error, }, }; case 'Incident Line': return { name, icon: VERTICAL_LINE_CHART_ICON, itemStyle: { color: theme.error, }, }; case 'Status Area': return { name, icon: HORIZONTAL_BAR_CHART_ICON, itemStyle: { color: theme.error, }, }; default: return { name, icon: 'circle', color: theme.gray300, }; } }); return Legend({ theme, orient: 'horizontal', align: 'left', show: true, data: legendItems, inactiveColor: theme.gray200, }); }, [rule, series, theme]); } /** * The legend icon for the dashed horizontal lines in a chart, commonly for thresholds. */ const HORIZONTAL_DASHED_LINE_CHART_ICON = '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'; /** * The legend icon for the vertical lines in a chart, commonly for incidents, or releases. */ const VERTICAL_LINE_CHART_ICON = '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'; /** * The legend icon for the thick horizontal bar along y=0 in a chart, commonly for status areas in metric alerts. */ const HORIZONTAL_BAR_CHART_ICON = 'path://M180 1000 l0 -160 400 0 400 0 0 160 0 160 -400 0 -400 0 0 -160z';