detailsSidebar.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import {Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import ActorAvatar from 'sentry/components/avatar/actorAvatar';
  4. import {SectionHeading} from 'sentry/components/charts/styles';
  5. import {KeyValueTable, KeyValueTableRow} from 'sentry/components/keyValueTable';
  6. import Text from 'sentry/components/text';
  7. import TimeSince from 'sentry/components/timeSince';
  8. import {Tooltip} from 'sentry/components/tooltip';
  9. import {IconCopy} from 'sentry/icons';
  10. import {t, tn} from 'sentry/locale';
  11. import {space} from 'sentry/styles/space';
  12. import {getFormattedDate} from 'sentry/utils/dates';
  13. import useCopyToClipboard from 'sentry/utils/useCopyToClipboard';
  14. import {
  15. DEFAULT_CHECKIN_MARGIN,
  16. DEFAULT_MAX_RUNTIME,
  17. } from 'sentry/views/monitors/components/monitorForm';
  18. import {MonitorIndicator} from 'sentry/views/monitors/components/monitorIndicator';
  19. import type {Monitor, MonitorEnvironment} from 'sentry/views/monitors/types';
  20. import {CheckInStatus, ScheduleType} from 'sentry/views/monitors/types';
  21. import {scheduleAsText} from 'sentry/views/monitors/utils/scheduleAsText';
  22. interface Props {
  23. monitor: Monitor;
  24. monitorEnv?: MonitorEnvironment;
  25. }
  26. export default function DetailsSidebar({monitorEnv, monitor}: Props) {
  27. const {checkin_margin, schedule, schedule_type, max_runtime, timezone} = monitor.config;
  28. const {onClick, label} = useCopyToClipboard({text: monitor.slug});
  29. const slug = (
  30. <Tooltip title={label}>
  31. <MonitorSlug onClick={onClick}>
  32. <SlugText>{monitor.slug}</SlugText>
  33. <IconCopy size="xs" />
  34. </MonitorSlug>
  35. </Tooltip>
  36. );
  37. return (
  38. <Fragment>
  39. <CheckIns>
  40. <SectionHeading>{t('Last Check-In')}</SectionHeading>
  41. <SectionHeading>{t('Next Check-In')}</SectionHeading>
  42. <div>
  43. {monitorEnv?.lastCheckIn ? (
  44. <TimeSince
  45. unitStyle="regular"
  46. liveUpdateInterval="second"
  47. date={monitorEnv.lastCheckIn}
  48. />
  49. ) : (
  50. '-'
  51. )}
  52. </div>
  53. <div>
  54. {monitor.status !== 'disabled' && monitorEnv?.nextCheckIn ? (
  55. <TimeSince
  56. unitStyle="regular"
  57. liveUpdateInterval="second"
  58. date={monitorEnv.nextCheckIn}
  59. />
  60. ) : (
  61. '-'
  62. )}
  63. </div>
  64. </CheckIns>
  65. <SectionHeading>{t('Schedule')}</SectionHeading>
  66. <Schedule>
  67. <Text>
  68. {scheduleAsText(monitor.config)}{' '}
  69. {schedule_type === ScheduleType.CRONTAB && `(${timezone})`}
  70. </Text>
  71. {schedule_type === ScheduleType.CRONTAB && (
  72. <CrontabText>({schedule})</CrontabText>
  73. )}
  74. </Schedule>
  75. <SectionHeading>{t('Legend')}</SectionHeading>
  76. <Thresholds>
  77. <MonitorIndicator status={CheckInStatus.MISSED} size={12} />
  78. <Text>
  79. {tn(
  80. 'Check-in missed after %s min',
  81. 'Check-in missed after %s mins',
  82. checkin_margin ?? DEFAULT_CHECKIN_MARGIN
  83. )}
  84. </Text>
  85. <MonitorIndicator status={CheckInStatus.ERROR} size={12} />
  86. <Text>{t('Check-in reported as failed')}</Text>
  87. <MonitorIndicator status={CheckInStatus.TIMEOUT} size={12} />
  88. <Text>
  89. {tn(
  90. 'Check-in timed out after %s min',
  91. 'Check-in timed out after %s mins',
  92. max_runtime ?? DEFAULT_MAX_RUNTIME
  93. )}
  94. </Text>
  95. </Thresholds>
  96. <SectionHeading>{t('Cron Details')}</SectionHeading>
  97. <KeyValueTable>
  98. <KeyValueTableRow keyName={t('Monitor Slug')} value={slug} />
  99. <KeyValueTableRow
  100. keyName={t('Owner')}
  101. value={
  102. monitor.owner ? (
  103. <ActorAvatar size={24} actor={monitor.owner} />
  104. ) : (
  105. t('Unassigned')
  106. )
  107. }
  108. />
  109. <KeyValueTableRow
  110. keyName={t('Date created')}
  111. value={getFormattedDate(monitor.dateCreated, 'MMM D, YYYY')}
  112. />
  113. </KeyValueTable>
  114. </Fragment>
  115. );
  116. }
  117. const CheckIns = styled('div')`
  118. display: grid;
  119. grid-template-columns: 1fr 1fr;
  120. margin-bottom: ${space(2)};
  121. `;
  122. const Schedule = styled('div')`
  123. margin-bottom: ${space(2)};
  124. display: flex;
  125. flex-wrap: wrap;
  126. gap: ${space(1)};
  127. `;
  128. const CrontabText = styled(Text)`
  129. font-family: ${p => p.theme.text.familyMono};
  130. color: ${p => p.theme.subText};
  131. `;
  132. const Thresholds = styled('div')`
  133. display: grid;
  134. grid-template-columns: max-content 1fr;
  135. margin-bottom: ${space(2)};
  136. align-items: center;
  137. gap: ${space(1)};
  138. `;
  139. const MonitorSlug = styled('button')`
  140. display: grid;
  141. grid-template-columns: 1fr max-content;
  142. align-items: center;
  143. gap: ${space(0.5)};
  144. background: transparent;
  145. border: none;
  146. &:hover {
  147. color: ${p => p.theme.textColor};
  148. }
  149. `;
  150. const SlugText = styled(Text)`
  151. white-space: nowrap;
  152. overflow: hidden;
  153. text-overflow: ellipsis;
  154. `;