details.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import styled from '@emotion/styled';
  2. import ActorAvatar from 'sentry/components/avatar/actorAvatar';
  3. import Breadcrumbs from 'sentry/components/breadcrumbs';
  4. import {LinkButton} from 'sentry/components/button';
  5. import ButtonBar from 'sentry/components/buttonBar';
  6. import {SectionHeading} from 'sentry/components/charts/styles';
  7. import {CodeSnippet} from 'sentry/components/codeSnippet';
  8. import IdBadge from 'sentry/components/idBadge';
  9. import {KeyValueTable, KeyValueTableRow} from 'sentry/components/keyValueTable';
  10. import * as Layout from 'sentry/components/layouts/thirds';
  11. import LoadingError from 'sentry/components/loadingError';
  12. import LoadingIndicator from 'sentry/components/loadingIndicator';
  13. import {DatePageFilter} from 'sentry/components/organizations/datePageFilter';
  14. import {EnvironmentPageFilter} from 'sentry/components/organizations/environmentPageFilter';
  15. import PageFilterBar from 'sentry/components/organizations/pageFilterBar';
  16. import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
  17. import {IconEdit} from 'sentry/icons';
  18. import {t} from 'sentry/locale';
  19. import {space} from 'sentry/styles/space';
  20. import type {RouteComponentProps} from 'sentry/types/legacyReactRouter';
  21. import getDuration from 'sentry/utils/duration/getDuration';
  22. import {type ApiQueryKey, useApiQuery} from 'sentry/utils/queryClient';
  23. import useOrganization from 'sentry/utils/useOrganization';
  24. import useProjects from 'sentry/utils/useProjects';
  25. import type {UptimeRule} from 'sentry/views/alerts/rules/uptime/types';
  26. import {UptimeIssues} from './uptimeIssues';
  27. interface UptimeAlertDetailsProps
  28. extends RouteComponentProps<{projectId: string; uptimeRuleId: string}, {}> {}
  29. export default function UptimeAlertDetails({params}: UptimeAlertDetailsProps) {
  30. const organization = useOrganization();
  31. const {projectId, uptimeRuleId} = params;
  32. const {projects, fetching: loadingProject} = useProjects({slugs: [projectId]});
  33. const project = projects.find(({slug}) => slug === projectId);
  34. const queryKey: ApiQueryKey = [
  35. `/projects/${organization.slug}/${projectId}/uptime/${uptimeRuleId}/`,
  36. ];
  37. const {
  38. data: uptimeRule,
  39. isPending,
  40. isError,
  41. } = useApiQuery<UptimeRule>(queryKey, {staleTime: 0});
  42. if (isError) {
  43. return (
  44. <LoadingError
  45. message={t('The uptime alert rule you were looking for was not found.')}
  46. />
  47. );
  48. }
  49. if (isPending || loadingProject) {
  50. return (
  51. <Layout.Body>
  52. <Layout.Main fullWidth>
  53. <LoadingIndicator />
  54. </Layout.Main>
  55. </Layout.Body>
  56. );
  57. }
  58. if (!project) {
  59. return (
  60. <LoadingError message={t('The project you were looking for was not found.')} />
  61. );
  62. }
  63. return (
  64. <Layout.Page>
  65. <SentryDocumentTitle title={`${uptimeRule.name} — Alerts`} />
  66. <Layout.Header>
  67. <Layout.HeaderContent>
  68. <Breadcrumbs
  69. crumbs={[
  70. {
  71. label: t('Alerts'),
  72. to: `/organizations/${organization.slug}/alerts/rules/`,
  73. },
  74. {
  75. label: t('Uptime Monitor'),
  76. },
  77. ]}
  78. />
  79. <Layout.Title>
  80. <IdBadge
  81. project={project}
  82. avatarSize={28}
  83. hideName
  84. avatarProps={{hasTooltip: true, tooltip: project.slug}}
  85. />
  86. {uptimeRule.name}
  87. </Layout.Title>
  88. </Layout.HeaderContent>
  89. <Layout.HeaderActions>
  90. <ButtonBar gap={1}>
  91. <LinkButton
  92. size="sm"
  93. icon={<IconEdit />}
  94. to={`/organizations/${organization.slug}/alerts/uptime-rules/${project.slug}/${uptimeRuleId}/`}
  95. >
  96. {t('Edit Rule')}
  97. </LinkButton>
  98. </ButtonBar>
  99. </Layout.HeaderActions>
  100. </Layout.Header>
  101. <Layout.Body>
  102. <Layout.Main>
  103. <StyledPageFilterBar condensed>
  104. <DatePageFilter />
  105. <EnvironmentPageFilter />
  106. </StyledPageFilterBar>
  107. <UptimeIssues project={project} ruleId={uptimeRuleId} />
  108. </Layout.Main>
  109. <Layout.Side>
  110. <SectionHeading>{t('Checked URL')}</SectionHeading>
  111. <CodeSnippet
  112. hideCopyButton
  113. >{`${uptimeRule.method} ${uptimeRule.url}`}</CodeSnippet>
  114. <SectionHeading>{t('Configuration')}</SectionHeading>
  115. <KeyValueTable>
  116. <KeyValueTableRow
  117. keyName={t('Check Interval')}
  118. value={t('Every %s', getDuration(uptimeRule.intervalSeconds))}
  119. />
  120. <KeyValueTableRow
  121. keyName={t('Timeout')}
  122. value={t('After %s', getDuration(uptimeRule.timeoutMs / 1000, 2))}
  123. />
  124. <KeyValueTableRow keyName={t('Environment')} value={uptimeRule.environment} />
  125. <KeyValueTableRow
  126. keyName={t('Owner')}
  127. value={
  128. uptimeRule.owner ? (
  129. <ActorAvatar actor={uptimeRule.owner} />
  130. ) : (
  131. t('Unassigned')
  132. )
  133. }
  134. />
  135. </KeyValueTable>
  136. </Layout.Side>
  137. </Layout.Body>
  138. </Layout.Page>
  139. );
  140. }
  141. const StyledPageFilterBar = styled(PageFilterBar)`
  142. margin-bottom: ${space(2)};
  143. `;