import type {LineSeriesOption} from 'echarts'; import {BarChart, BarChartSeries} from 'sentry/components/charts/barChart'; import {getYAxisMaxFn} from 'sentry/components/charts/miniBarChart'; import LineSeries from 'sentry/components/charts/series/lineSeries'; import EmptyMessage from 'sentry/components/emptyMessage'; import {Panel, PanelBody} from 'sentry/components/panels'; import {t} from 'sentry/locale'; import {intervalToMilliseconds} from 'sentry/utils/dates'; import {axisLabelFormatter, tooltipFormatter} from 'sentry/utils/discover/charts'; import {AggregationOutputType} from 'sentry/utils/discover/fields'; import theme from 'sentry/utils/theme'; import useApiRequests from 'sentry/utils/useApiRequests'; import usePageFilters from 'sentry/utils/usePageFilters'; import {Monitor, MonitorStat} from '../types'; type Props = { monitor: Monitor; orgId: string; }; type State = { stats: MonitorStat[] | null; }; const MonitorStats = ({monitor, orgId}: Props) => { const {selection} = usePageFilters(); const {start, end, period} = selection.datetime; let since: number, until: number; if (start && end) { until = new Date(end).getTime() / 1000; since = new Date(start).getTime() / 1000; } else { until = Math.floor(new Date().getTime() / 1000); const intervalSeconds = intervalToMilliseconds(period ?? '30d') / 1000; since = until - intervalSeconds; } const {data, renderComponent} = useApiRequests({ endpoints: [ [ 'stats', `/organizations/${orgId}/monitors/${monitor.slug}/stats/`, { query: { since: since.toString(), until: until.toString(), resolution: '1d', }, }, ], ], }); let emptyStats = true; const success: BarChartSeries = { seriesName: t('Successful'), yAxisIndex: 0, data: [], }; const failed: BarChartSeries = { seriesName: t('Failed'), yAxisIndex: 0, data: [], }; const missed: BarChartSeries = { seriesName: t('Missed'), yAxisIndex: 0, data: [], }; const durationData = [] as [number, number][]; data.stats?.forEach(p => { if (p.ok || p.error || p.missed) { emptyStats = false; } const timestamp = p.ts * 1000; success.data.push({name: timestamp, value: p.ok}); failed.data.push({name: timestamp, value: p.error}); missed.data.push({name: timestamp, value: p.missed}); durationData.push([timestamp, Math.trunc(p.duration)]); }); const colors = [theme.green200, theme.red200, theme.yellow200]; const durationTitle = t('Average Duration'); const additionalSeries: LineSeriesOption[] = [ LineSeries({ name: durationTitle, data: durationData, lineStyle: {color: theme.purple300, width: 2}, itemStyle: {color: theme.purple300}, yAxisIndex: 1, animation: false, }), ]; const height = 150; const getYAxisOptions = (aggregateType: AggregationOutputType) => ({ max: getYAxisMaxFn(height), splitLine: { show: false, }, axisLabel: { formatter: (value: number) => axisLabelFormatter(value, aggregateType, true), showMaxLabel: false, }, }); return renderComponent( {!emptyStats ? ( { return label === durationTitle ? tooltipFormatter(value, 'duration') : tooltipFormatter(value, 'number'); }, }} yAxes={[{...getYAxisOptions('number')}, {...getYAxisOptions('duration')}]} grid={{ top: 6, bottom: 0, left: 4, right: 0, }} animation={false} /> ) : ( )} ); }; export default MonitorStats;