health.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import {Fragment} from 'react';
  2. import {RouteComponentProps} from 'react-router';
  3. import styled from '@emotion/styled';
  4. import * as Layout from 'sentry/components/layouts/thirds';
  5. import LoadingIndicator from 'sentry/components/loadingIndicator';
  6. import NoProjectMessage from 'sentry/components/noProjectMessage';
  7. import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
  8. import {t} from 'sentry/locale';
  9. import {TeamWithProjects} from 'sentry/types';
  10. import localStorage from 'sentry/utils/localStorage';
  11. import useRouteAnalyticsEventNames from 'sentry/utils/routeAnalytics/useRouteAnalyticsEventNames';
  12. import useOrganization from 'sentry/utils/useOrganization';
  13. import {useTeams} from 'sentry/utils/useTeams';
  14. import Header from '../header';
  15. import TeamStatsControls from './controls';
  16. import DescriptionCard from './descriptionCard';
  17. import TeamAlertsTriggered from './teamAlertsTriggered';
  18. import TeamMisery from './teamMisery';
  19. import TeamReleases from './teamReleases';
  20. import TeamStability from './teamStability';
  21. import {dataDatetime} from './utils';
  22. type Props = RouteComponentProps<{}, {}>;
  23. function TeamStatsHealth({location, router}: Props) {
  24. const organization = useOrganization();
  25. const {teams, initiallyLoaded} = useTeams({provideUserTeams: true});
  26. useRouteAnalyticsEventNames('team_insights.viewed', 'Team Insights: Viewed');
  27. const query = location?.query ?? {};
  28. const localStorageKey = `teamInsightsSelectedTeamId:${organization.slug}`;
  29. let localTeamId: string | null | undefined =
  30. query.team ?? localStorage.getItem(localStorageKey);
  31. if (localTeamId && !teams.find(team => team.id === localTeamId)) {
  32. localTeamId = null;
  33. }
  34. const currentTeamId = localTeamId ?? teams[0]?.id;
  35. const currentTeam = teams.find(team => team.id === currentTeamId) as
  36. | TeamWithProjects
  37. | undefined;
  38. const projects = currentTeam?.projects ?? [];
  39. const {period, start, end, utc} = dataDatetime(query);
  40. if (teams.length === 0) {
  41. return (
  42. <NoProjectMessage organization={organization} superuserNeedsToBeProjectMember />
  43. );
  44. }
  45. return (
  46. <Fragment>
  47. <SentryDocumentTitle title={t('Project Health')} orgSlug={organization.slug} />
  48. <Header organization={organization} activeTab="health" />
  49. <Body>
  50. <TeamStatsControls
  51. location={location}
  52. router={router}
  53. currentTeam={currentTeam}
  54. />
  55. {!initiallyLoaded && <LoadingIndicator />}
  56. {initiallyLoaded && (
  57. <Layout.Main fullWidth>
  58. <DescriptionCard
  59. title={t('Crash Free Sessions')}
  60. description={t(
  61. 'The percentage of healthy, errored, and abnormal sessions that didn’t cause a crash.'
  62. )}
  63. >
  64. <TeamStability
  65. projects={projects}
  66. organization={organization}
  67. period={period}
  68. start={start}
  69. end={end}
  70. utc={utc}
  71. />
  72. </DescriptionCard>
  73. <DescriptionCard
  74. title={t('User Misery')}
  75. description={t(
  76. 'The number of unique users that experienced load times 4x the project’s configured threshold.'
  77. )}
  78. >
  79. <TeamMisery
  80. organization={organization}
  81. projects={projects}
  82. teamId={currentTeam!.id}
  83. period={period}
  84. start={start?.toString()}
  85. end={end?.toString()}
  86. location={location}
  87. />
  88. </DescriptionCard>
  89. <DescriptionCard
  90. title={t('Metric Alerts Triggered')}
  91. description={t('Alerts triggered from the Alert Rules your team created.')}
  92. >
  93. <TeamAlertsTriggered
  94. organization={organization}
  95. projects={projects}
  96. teamSlug={currentTeam!.slug}
  97. period={period}
  98. start={start?.toString()}
  99. end={end?.toString()}
  100. />
  101. </DescriptionCard>
  102. <DescriptionCard
  103. title={t('Number of Releases')}
  104. description={t('The releases that were created in your team’s projects.')}
  105. >
  106. <TeamReleases
  107. projects={projects}
  108. organization={organization}
  109. teamSlug={currentTeam!.slug}
  110. period={period}
  111. start={start}
  112. end={end}
  113. utc={utc}
  114. />
  115. </DescriptionCard>
  116. </Layout.Main>
  117. )}
  118. </Body>
  119. </Fragment>
  120. );
  121. }
  122. export default TeamStatsHealth;
  123. const Body = styled(Layout.Body)`
  124. @media (min-width: ${p => p.theme.breakpoints.medium}) {
  125. display: block;
  126. }
  127. `;