checkInTimeline.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import {Theme} from '@emotion/react';
  2. import styled from '@emotion/styled';
  3. import DateTime from 'sentry/components/dateTime';
  4. import {Resizeable} from 'sentry/components/replays/resizeable';
  5. import {Tooltip} from 'sentry/components/tooltip';
  6. import {space} from 'sentry/styles/space';
  7. import {MonitorBucketData} from 'sentry/views/monitors/components/overviewTimeline/types';
  8. import {CheckInStatus} from 'sentry/views/monitors/types';
  9. import {getAggregateStatus} from 'sentry/views/monitors/utils/getAggregateStatus';
  10. import {mergeBuckets} from 'sentry/views/monitors/utils/mergeBuckets';
  11. interface Props {
  12. bucketedData: MonitorBucketData;
  13. end: Date;
  14. start: Date;
  15. width?: number;
  16. }
  17. function getColorFromStatus(status: CheckInStatus, theme: Theme) {
  18. const statusToColor: Record<CheckInStatus, string> = {
  19. [CheckInStatus.ERROR]: theme.red200,
  20. [CheckInStatus.TIMEOUT]: theme.red200,
  21. [CheckInStatus.OK]: theme.green200,
  22. [CheckInStatus.MISSED]: theme.yellow200,
  23. [CheckInStatus.IN_PROGRESS]: theme.disabled,
  24. };
  25. return statusToColor[status];
  26. }
  27. function getBucketedCheckInsPosition(
  28. timestamp: number,
  29. timelineStart: Date,
  30. msPerPixel: number
  31. ) {
  32. const elapsedSinceStart = new Date(timestamp).getTime() - timelineStart.getTime();
  33. return elapsedSinceStart / msPerPixel;
  34. }
  35. export function CheckInTimeline(props: Props) {
  36. const {bucketedData, start, end} = props;
  37. function renderTimelineWithWidth(width: number) {
  38. const timeWindow = end.getTime() - start.getTime();
  39. const msPerPixel = timeWindow / width;
  40. const jobTicks = mergeBuckets(bucketedData);
  41. return (
  42. <TimelineContainer>
  43. {jobTicks.map(
  44. ({startTs, width: tickWidth, envMapping, roundedLeft, roundedRight}) => {
  45. const timestampMs = startTs * 1000;
  46. const left = getBucketedCheckInsPosition(timestampMs, start, msPerPixel);
  47. return (
  48. <JobTickContainer style={{left}} key={startTs}>
  49. <Tooltip title={<DateTime date={timestampMs} seconds />}>
  50. <JobTick
  51. style={{width: tickWidth}}
  52. status={getAggregateStatus(envMapping)}
  53. roundedLeft={roundedLeft}
  54. roundedRight={roundedRight}
  55. />
  56. </Tooltip>
  57. </JobTickContainer>
  58. );
  59. }
  60. )}
  61. </TimelineContainer>
  62. );
  63. }
  64. if (props.width) {
  65. return renderTimelineWithWidth(props.width);
  66. }
  67. return <Resizeable>{({width}) => renderTimelineWithWidth(width)}</Resizeable>;
  68. }
  69. const TimelineContainer = styled('div')`
  70. position: relative;
  71. height: 14px;
  72. margin: ${space(2)} 0;
  73. `;
  74. const JobTickContainer = styled('div')`
  75. position: absolute;
  76. `;
  77. const JobTick = styled('div')<{
  78. roundedLeft: boolean;
  79. roundedRight: boolean;
  80. status: CheckInStatus;
  81. }>`
  82. background: ${p => getColorFromStatus(p.status, p.theme)};
  83. width: 4px;
  84. height: 14px;
  85. ${p =>
  86. p.roundedLeft &&
  87. `
  88. border-top-left-radius: 2px;
  89. border-bottom-left-radius: 2px;
  90. `}
  91. ${p =>
  92. p.roundedRight &&
  93. `
  94. border-top-right-radius: 2px;
  95. border-bottom-right-radius: 2px;
  96. `}
  97. `;