detailsTimeline.tsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import {useRef} from 'react';
  2. import styled from '@emotion/styled';
  3. import {
  4. deleteMonitorEnvironment,
  5. setEnvironmentIsMuted,
  6. } from 'sentry/actionCreators/monitors';
  7. import Panel from 'sentry/components/panels/panel';
  8. import Text from 'sentry/components/text';
  9. import {t} from 'sentry/locale';
  10. import {space} from 'sentry/styles/space';
  11. import type {Organization} from 'sentry/types';
  12. import {setApiQueryData, useQueryClient} from 'sentry/utils/queryClient';
  13. import useApi from 'sentry/utils/useApi';
  14. import {useDimensions} from 'sentry/utils/useDimensions';
  15. import useRouter from 'sentry/utils/useRouter';
  16. import type {Monitor} from 'sentry/views/monitors/types';
  17. import {makeMonitorDetailsQueryKey} from 'sentry/views/monitors/utils';
  18. import {OverviewRow} from './overviewTimeline/overviewRow';
  19. import {GridLineLabels, GridLineOverlay} from './timeline/gridLines';
  20. import {useMonitorStats} from './timeline/hooks/useMonitorStats';
  21. import {useTimeWindowConfig} from './timeline/hooks/useTimeWindowConfig';
  22. interface Props {
  23. monitor: Monitor;
  24. organization: Organization;
  25. }
  26. export function DetailsTimeline({monitor, organization}: Props) {
  27. const {location} = useRouter();
  28. const api = useApi();
  29. const queryClient = useQueryClient();
  30. const elementRef = useRef<HTMLDivElement>(null);
  31. const {width: timelineWidth} = useDimensions<HTMLDivElement>({elementRef});
  32. const timeWindowConfig = useTimeWindowConfig({timelineWidth});
  33. const {data: monitorStats, isLoading} = useMonitorStats({
  34. monitors: [monitor.id],
  35. timeWindowConfig,
  36. });
  37. const monitorDetailsQueryKey = makeMonitorDetailsQueryKey(
  38. organization,
  39. monitor.project.slug,
  40. monitor.slug,
  41. {...location.query}
  42. );
  43. const handleDeleteEnvironment = async (env: string) => {
  44. const success = await deleteMonitorEnvironment(api, organization.slug, monitor, env);
  45. if (!success) {
  46. return;
  47. }
  48. setApiQueryData(queryClient, monitorDetailsQueryKey, (oldMonitorDetails: Monitor) => {
  49. const newEnvList = oldMonitorDetails.environments.filter(e => e.name !== env);
  50. const newMonitorDetails = {
  51. ...oldMonitorDetails,
  52. environments: newEnvList,
  53. };
  54. return newMonitorDetails;
  55. });
  56. };
  57. const handleToggleMuteEnvironment = async (env: string, isMuted: boolean) => {
  58. const resp = await setEnvironmentIsMuted(
  59. api,
  60. organization.slug,
  61. monitor,
  62. env,
  63. isMuted
  64. );
  65. if (resp === null) {
  66. return;
  67. }
  68. setApiQueryData(queryClient, monitorDetailsQueryKey, (oldMonitorDetails: Monitor) => {
  69. const oldMonitorEnvIdx = oldMonitorDetails.environments.findIndex(
  70. monitorEnv => monitorEnv.name === env
  71. );
  72. if (oldMonitorEnvIdx < 0) {
  73. return oldMonitorDetails;
  74. }
  75. oldMonitorDetails.environments[oldMonitorEnvIdx] = {
  76. ...oldMonitorDetails.environments[oldMonitorEnvIdx],
  77. isMuted,
  78. };
  79. return oldMonitorDetails;
  80. });
  81. };
  82. return (
  83. <TimelineContainer>
  84. <TimelineWidthTracker ref={elementRef} />
  85. <Header>
  86. <TimelineTitle>{t('Check-Ins')}</TimelineTitle>
  87. <GridLineLabels timeWindowConfig={timeWindowConfig} width={timelineWidth} />
  88. </Header>
  89. <AlignedGridLineOverlay
  90. allowZoom={!isLoading}
  91. showCursor={!isLoading}
  92. timeWindowConfig={timeWindowConfig}
  93. width={timelineWidth}
  94. />
  95. <OverviewRow
  96. monitor={monitor}
  97. bucketedData={monitorStats?.[monitor.id]}
  98. timeWindowConfig={timeWindowConfig}
  99. width={timelineWidth}
  100. onDeleteEnvironment={handleDeleteEnvironment}
  101. onToggleMuteEnvironment={handleToggleMuteEnvironment}
  102. singleMonitorView
  103. />
  104. </TimelineContainer>
  105. );
  106. }
  107. const TimelineContainer = styled(Panel)`
  108. display: grid;
  109. grid-template-columns: 135px 1fr;
  110. `;
  111. const Header = styled('div')`
  112. grid-column: 1/-1;
  113. display: grid;
  114. grid-template-columns: subgrid;
  115. border-bottom: 1px solid ${p => p.theme.border};
  116. `;
  117. const TimelineWidthTracker = styled('div')`
  118. position: absolute;
  119. width: 100%;
  120. grid-row: 1;
  121. grid-column: 2;
  122. `;
  123. const AlignedGridLineOverlay = styled(GridLineOverlay)`
  124. grid-column: 2;
  125. `;
  126. const TimelineTitle = styled(Text)`
  127. ${p => p.theme.text.cardTitle};
  128. padding: ${space(2)};
  129. grid-column: 1;
  130. `;