import {Fragment} from 'react'; import {css} from '@emotion/react'; import styled from '@emotion/styled'; import round from 'lodash/round'; import {Button} from 'sentry/components/button'; import {BarChart} from 'sentry/components/charts/barChart'; import type {DateTimeObject} from 'sentry/components/charts/utils'; import Link from 'sentry/components/links/link'; import LoadingError from 'sentry/components/loadingError'; import LoadingIndicator from 'sentry/components/loadingIndicator'; import {normalizeDateTimeParams} from 'sentry/components/organizations/pageFilters/parse'; import {PanelTable} from 'sentry/components/panels/panelTable'; import {IconArrow} from 'sentry/icons'; import {t, tct} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import type {Organization} from 'sentry/types/organization'; import type {Project} from 'sentry/types/project'; import {formatPercentage} from 'sentry/utils/number/formatPercentage'; import {useApiQuery} from 'sentry/utils/queryClient'; import type {ColorOrAlias} from 'sentry/utils/theme'; import type {MetricRule} from 'sentry/views/alerts/rules/metric/types'; import {ProjectBadge, ProjectBadgeContainer} from './styles'; import {barAxisLabel, convertDayValueObjectToSeries, sortSeriesByDay} from './utils'; export type AlertsTriggered = Record; type AlertsTriggeredRule = MetricRule & { totalThisWeek: number; weeklyAvg: number; }; interface TeamAlertsTriggeredProps extends DateTimeObject { organization: Organization; projects: Project[]; teamSlug: string; } function TeamAlertsTriggered({ organization, projects, teamSlug, start, end, period, utc, }: TeamAlertsTriggeredProps) { const datetime = {start, end, period, utc}; const { data: alertsTriggered, isLoading: isAlertsTriggeredLoading, isError: isAlertsTriggeredError, refetch: refetchAlertsTriggered, } = useApiQuery( [ `/teams/${organization.slug}/${teamSlug}/alerts-triggered/`, { query: { ...normalizeDateTimeParams(datetime), }, }, ], {staleTime: 5000} ); const { data: alertsTriggeredRules, isLoading: isAlertsTriggeredRulesLoading, isError: isAlertsTriggeredRulesError, refetch: refetchAlertsTriggeredRule, } = useApiQuery( [ `/teams/${organization.slug}/${teamSlug}/alerts-triggered-index/`, { query: { ...normalizeDateTimeParams(datetime), }, }, ], {staleTime: 5000} ); if (isAlertsTriggeredLoading || isAlertsTriggeredRulesLoading) { return ( ); } if (isAlertsTriggeredError || isAlertsTriggeredRulesError) { return ( { refetchAlertsTriggered(); refetchAlertsTriggeredRule(); }} /> ); } function renderTrend(rule: AlertsTriggeredRule) { const {weeklyAvg, totalThisWeek} = rule; const diff = totalThisWeek - weeklyAvg; // weeklyAvg can only be 0 only if totalThisWeek is also 0 // but those should never be returned in alerts-triggered-index request if (weeklyAvg === 0) { return '\u2014'; } return ( {formatPercentage(Math.abs(diff / weeklyAvg), 0)} ); } const seriesData = sortSeriesByDay( convertDayValueObjectToSeries(alertsTriggered ?? {}) ); return ( } headers={[ t('Alert Rule'), t('Project'), {tct('Last [period] Average', {period})}, {t('This Week')}, {t('Difference')}, ]} > {alertsTriggeredRules?.map(rule => { const project = projects.find(p => p.slug === rule.projects[0]); return ( {rule.name} {project && } {round(rule.weeklyAvg, 2)} {rule.totalThisWeek} {renderTrend(rule)} ); })} ); } export default TeamAlertsTriggered; const ChartWrapper = styled('div')` padding: ${space(2)} ${space(2)} 0 ${space(2)}; border-bottom: 1px solid ${p => p.theme.border}; `; const StyledPanelTable = styled(PanelTable)` grid-template-columns: 1fr 0.5fr 0.2fr 0.2fr 0.2fr; font-size: ${p => p.theme.fontSizeMedium}; white-space: nowrap; margin-bottom: 0; border: 0; box-shadow: unset; & > div { padding: ${space(1)} ${space(2)}; } ${p => p.isEmpty && css` & > div:last-child { padding: 48px ${space(2)}; } `} `; const AlertNameContainer = styled('div')` ${p => p.theme.overflowEllipsis} `; const AlignRight = styled('div')` text-align: right; font-variant-numeric: tabular-nums; `; const PaddedIconArrow = styled(IconArrow)` margin: 0 ${space(0.5)}; `; const SubText = styled('div')<{color: ColorOrAlias}>` color: ${p => p.theme[p.color]}; `; const ButtonsContainer = styled('div')` & > a { margin-right: ${space(0.5)}; margin-left: ${space(0.5)}; } `;