index.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import {useCallback, useRef} from 'react';
  2. import styled from '@emotion/styled';
  3. import Panel from 'sentry/components/panels/panel';
  4. import {SegmentedControl} from 'sentry/components/segmentedControl';
  5. import {t} from 'sentry/locale';
  6. import {space} from 'sentry/styles/space';
  7. import {useApiQuery} from 'sentry/utils/queryClient';
  8. import {useDimensions} from 'sentry/utils/useDimensions';
  9. import useOrganization from 'sentry/utils/useOrganization';
  10. import useRouter from 'sentry/utils/useRouter';
  11. import {
  12. GridLineOverlay,
  13. GridLineTimeLabels,
  14. } from 'sentry/views/monitors/components/overviewTimeline/gridLines';
  15. import {TimelineTableRow} from 'sentry/views/monitors/components/overviewTimeline/timelineTableRow';
  16. import {Monitor} from '../../types';
  17. import {MonitorBucketData, TimeWindow} from './types';
  18. import {getStartFromTimeWindow, timeWindowConfig} from './utils';
  19. interface Props {
  20. monitorList: Monitor[];
  21. }
  22. export function OverviewTimeline({monitorList}: Props) {
  23. const {replace, location} = useRouter();
  24. const organization = useOrganization();
  25. const timeWindow: TimeWindow = location.query?.timeWindow ?? '24h';
  26. const nowRef = useRef<Date>(new Date());
  27. const start = getStartFromTimeWindow(nowRef.current, timeWindow);
  28. const {elementRef, width: timelineWidth} = useDimensions<HTMLDivElement>();
  29. const handleResolutionChange = useCallback(
  30. (value: TimeWindow) => {
  31. replace({...location, query: {...location.query, timeWindow: value}});
  32. },
  33. [location, replace]
  34. );
  35. const rollup = Math.floor(
  36. (timeWindowConfig[timeWindow].elapsedMinutes * 60) / timelineWidth
  37. );
  38. const monitorStatsQueryKey = `/organizations/${organization.slug}/monitors-stats/`;
  39. const {data: monitorStats, isLoading} = useApiQuery<Record<string, MonitorBucketData>>(
  40. [
  41. monitorStatsQueryKey,
  42. {
  43. query: {
  44. until: Math.floor(nowRef.current.getTime() / 1000),
  45. since: Math.floor(start.getTime() / 1000),
  46. monitor: monitorList.map(m => m.slug),
  47. resolution: `${rollup}s`,
  48. ...location.query,
  49. },
  50. },
  51. ],
  52. {
  53. staleTime: 0,
  54. enabled: timelineWidth > 0,
  55. }
  56. );
  57. return (
  58. <MonitorListPanel>
  59. <ListFilters>
  60. <SegmentedControl<TimeWindow>
  61. value={timeWindow}
  62. onChange={handleResolutionChange}
  63. size="xs"
  64. aria-label={t('Time Scale')}
  65. >
  66. <SegmentedControl.Item key="1h">{t('Hour')}</SegmentedControl.Item>
  67. <SegmentedControl.Item key="24h">{t('Day')}</SegmentedControl.Item>
  68. <SegmentedControl.Item key="7d">{t('Week')}</SegmentedControl.Item>
  69. <SegmentedControl.Item key="30d">{t('Month')}</SegmentedControl.Item>
  70. </SegmentedControl>
  71. </ListFilters>
  72. <TimelineWidthTracker ref={elementRef} />
  73. <GridLineTimeLabels
  74. timeWindow={timeWindow}
  75. end={nowRef.current}
  76. width={timelineWidth}
  77. />
  78. <GridLineOverlay
  79. showCursor={!isLoading}
  80. timeWindow={timeWindow}
  81. end={nowRef.current}
  82. width={timelineWidth}
  83. />
  84. {monitorList.map(monitor => (
  85. <TimelineTableRow
  86. key={monitor.id}
  87. monitor={monitor}
  88. timeWindow={timeWindow}
  89. bucketedData={monitorStats?.[monitor.slug]}
  90. end={nowRef.current}
  91. start={start}
  92. width={timelineWidth}
  93. />
  94. ))}
  95. </MonitorListPanel>
  96. );
  97. }
  98. const MonitorListPanel = styled(Panel)`
  99. display: grid;
  100. grid-template-columns: 350px 135px 1fr;
  101. `;
  102. const ListFilters = styled('div')`
  103. display: flex;
  104. gap: ${space(1)};
  105. padding: ${space(1.5)} ${space(2)};
  106. border-bottom: 1px solid ${p => p.theme.border};
  107. grid-column: 1/3;
  108. `;
  109. const TimelineWidthTracker = styled('div')`
  110. position: absolute;
  111. width: 100%;
  112. grid-row: 1;
  113. grid-column: 3;
  114. `;