monitorHeader.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import styled from '@emotion/styled';
  2. import Breadcrumbs from 'sentry/components/breadcrumbs';
  3. import {SectionHeading} from 'sentry/components/charts/styles';
  4. import Clipboard from 'sentry/components/clipboard';
  5. import IdBadge from 'sentry/components/idBadge';
  6. import * as Layout from 'sentry/components/layouts/thirds';
  7. import TimeSince from 'sentry/components/timeSince';
  8. import {IconCopy} from 'sentry/icons';
  9. import {t} from 'sentry/locale';
  10. import {space} from 'sentry/styles/space';
  11. import {Monitor, MonitorEnvironment, MonitorStatus} from '../types';
  12. import MonitorHeaderActions from './monitorHeaderActions';
  13. import MonitorIcon from './monitorIcon';
  14. interface Props {
  15. monitor: Monitor;
  16. onUpdate: (data: Monitor) => void;
  17. orgId: string;
  18. monitorEnv?: MonitorEnvironment;
  19. }
  20. const statusToLabel: Record<MonitorStatus, string> = {
  21. ok: t('Ok'),
  22. error: t('Failed'),
  23. disabled: t('Disabled'),
  24. active: t('Active'),
  25. missed_checkin: t('Missed'),
  26. };
  27. function MonitorHeader({monitor, monitorEnv, orgId, onUpdate}: Props) {
  28. const crumbs = [
  29. {
  30. label: t('Crons'),
  31. to: `/organizations/${orgId}/crons/`,
  32. },
  33. {
  34. label: t('Cron Monitor Details'),
  35. },
  36. ];
  37. return (
  38. <Layout.Header>
  39. <Layout.HeaderContent>
  40. <Breadcrumbs crumbs={crumbs} />
  41. <Layout.Title>
  42. <IdBadge
  43. project={monitor.project}
  44. avatarSize={28}
  45. hideName
  46. avatarProps={{hasTooltip: true, tooltip: monitor.project.slug}}
  47. />
  48. {monitor.name}
  49. </Layout.Title>
  50. <Clipboard value={monitor.slug}>
  51. <MonitorSlug>
  52. {monitor.slug} <IconCopy size="xs" />
  53. </MonitorSlug>
  54. </Clipboard>
  55. </Layout.HeaderContent>
  56. <Layout.HeaderActions>
  57. <MonitorHeaderActions orgId={orgId} monitor={monitor} onUpdate={onUpdate} />
  58. <MonitorStats>
  59. <MonitorStatLabel>{t('Last Check-in')}</MonitorStatLabel>
  60. <MonitorStatLabel>{t('Next Check-in')}</MonitorStatLabel>
  61. <MonitorStatLabel>{t('Status')}</MonitorStatLabel>
  62. <div>
  63. {monitorEnv?.lastCheckIn && (
  64. <TimeSince
  65. unitStyle="regular"
  66. liveUpdateInterval="second"
  67. date={monitorEnv.lastCheckIn}
  68. />
  69. )}
  70. </div>
  71. <div>
  72. {monitorEnv?.nextCheckIn && (
  73. <TimeSince
  74. unitStyle="regular"
  75. liveUpdateInterval="second"
  76. date={monitorEnv.nextCheckIn}
  77. />
  78. )}
  79. </div>
  80. <div>
  81. {monitorEnv?.status && (
  82. <Status>
  83. <MonitorIcon status={monitorEnv.status} size={16} />
  84. <MonitorStatusLabel>
  85. {statusToLabel[monitorEnv.status]}
  86. </MonitorStatusLabel>
  87. </Status>
  88. )}
  89. </div>
  90. </MonitorStats>
  91. </Layout.HeaderActions>
  92. </Layout.Header>
  93. );
  94. }
  95. const MonitorSlug = styled('div')`
  96. margin-top: ${space(1)};
  97. color: ${p => p.theme.subText};
  98. cursor: pointer;
  99. width: max-content;
  100. `;
  101. const MonitorStats = styled('div')`
  102. display: grid;
  103. align-self: flex-end;
  104. grid-template-columns: repeat(3, max-content);
  105. grid-column-gap: ${space(4)};
  106. grid-row-gap: ${space(0.5)};
  107. margin-bottom: ${space(2)};
  108. margin-top: ${space(1)};
  109. `;
  110. const MonitorStatLabel = styled(SectionHeading)`
  111. text-transform: uppercase;
  112. font-size: ${p => p.theme.fontSizeSmall};
  113. text-align: center;
  114. `;
  115. const Status = styled('div')`
  116. display: flex;
  117. align-items: center;
  118. `;
  119. const MonitorStatusLabel = styled('div')`
  120. margin-left: ${space(1)};
  121. `;
  122. export default MonitorHeader;