content.tsx 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import {useCallback, useEffect} from 'react';
  2. import {browserHistory} from 'react-router';
  3. import styled from '@emotion/styled';
  4. import {Location} from 'history';
  5. import Alert from 'sentry/components/alert';
  6. import Button from 'sentry/components/button';
  7. import DatePageFilter from 'sentry/components/datePageFilter';
  8. import EnvironmentPageFilter from 'sentry/components/environmentPageFilter';
  9. import * as Layout from 'sentry/components/layouts/thirds';
  10. import NoProjectMessage from 'sentry/components/noProjectMessage';
  11. import PageFilterBar from 'sentry/components/organizations/pageFilterBar';
  12. import PageFiltersContainer from 'sentry/components/organizations/pageFilters/container';
  13. import PageHeading from 'sentry/components/pageHeading';
  14. import Pagination from 'sentry/components/pagination';
  15. import {ProfileTransactionsTable} from 'sentry/components/profiling/profileTransactionsTable';
  16. import ProjectPageFilter from 'sentry/components/projectPageFilter';
  17. import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
  18. import SmartSearchBar, {SmartSearchBarProps} from 'sentry/components/smartSearchBar';
  19. import {MAX_QUERY_LENGTH} from 'sentry/constants';
  20. import {t} from 'sentry/locale';
  21. import {PageContent} from 'sentry/styles/organization';
  22. import space from 'sentry/styles/space';
  23. import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent';
  24. import {useProfileFilters} from 'sentry/utils/profiling/hooks/useProfileFilters';
  25. import {useProfiles} from 'sentry/utils/profiling/hooks/useProfiles';
  26. import {useProfileTransactions} from 'sentry/utils/profiling/hooks/useProfileTransactions';
  27. import {generateProfilingOnboardingRoute} from 'sentry/utils/profiling/routes';
  28. import {decodeScalar} from 'sentry/utils/queryString';
  29. import useOrganization from 'sentry/utils/useOrganization';
  30. import usePageFilters from 'sentry/utils/usePageFilters';
  31. import {ProfilingScatterChart} from './landing/profilingScatterChart';
  32. interface ProfilingContentProps {
  33. location: Location;
  34. }
  35. function ProfilingContent({location}: ProfilingContentProps) {
  36. const organization = useOrganization();
  37. const {selection} = usePageFilters();
  38. const cursor = decodeScalar(location.query.cursor);
  39. const query = decodeScalar(location.query.query, '');
  40. const profileFilters = useProfileFilters({query: '', selection});
  41. const profiles = useProfiles({cursor, query, selection});
  42. const transactions = useProfileTransactions({cursor, query, selection});
  43. useEffect(() => {
  44. trackAdvancedAnalyticsEvent('profiling_views.landing', {
  45. organization,
  46. });
  47. }, [organization]);
  48. const handleSearch: SmartSearchBarProps['onSearch'] = useCallback(
  49. (searchQuery: string) => {
  50. browserHistory.push({
  51. ...location,
  52. query: {
  53. ...location.query,
  54. cursor: undefined,
  55. query: searchQuery || undefined,
  56. },
  57. });
  58. },
  59. [location]
  60. );
  61. const onSetupProfilingClick = useCallback(() => {
  62. browserHistory.push(generateProfilingOnboardingRoute({orgSlug: organization.slug}));
  63. }, [organization.slug]);
  64. return (
  65. <SentryDocumentTitle title={t('Profiling')} orgSlug={organization.slug}>
  66. <PageFiltersContainer>
  67. <NoProjectMessage organization={organization}>
  68. <StyledPageContent>
  69. <Layout.Header>
  70. <StyledLayoutHeaderContent>
  71. <StyledHeading>{t('Profiling')}</StyledHeading>
  72. <Button onClick={onSetupProfilingClick}>Setup Profiling</Button>
  73. </StyledLayoutHeaderContent>
  74. </Layout.Header>
  75. <Layout.Body>
  76. <Layout.Main fullWidth>
  77. <ActionBar>
  78. <PageFilterBar condensed>
  79. <ProjectPageFilter />
  80. <EnvironmentPageFilter />
  81. <DatePageFilter alignDropdown="left" />
  82. </PageFilterBar>
  83. <SmartSearchBar
  84. organization={organization}
  85. hasRecentSearches
  86. searchSource="profile_landing"
  87. supportedTags={profileFilters}
  88. query={query}
  89. onSearch={handleSearch}
  90. maxQueryLength={MAX_QUERY_LENGTH}
  91. />
  92. </ActionBar>
  93. {profiles.type === 'errored' && (
  94. <Alert type="error" showIcon>
  95. {t('Unable to load profiles')}
  96. </Alert>
  97. )}
  98. <ProfilingScatterChart
  99. datetime={
  100. selection?.datetime ?? {
  101. start: null,
  102. end: null,
  103. period: null,
  104. utc: null,
  105. }
  106. }
  107. traces={profiles.type === 'resolved' ? profiles.data.traces : []}
  108. isLoading={profiles.type === 'loading'}
  109. />
  110. <ProfileTransactionsTable
  111. error={
  112. transactions.type === 'errored' ? t('Unable to load profiles') : null
  113. }
  114. isLoading={transactions.type === 'loading'}
  115. transactions={
  116. transactions.type === 'resolved' ? transactions.data.transactions : []
  117. }
  118. />
  119. <Pagination
  120. pageLinks={
  121. transactions.type === 'resolved' ? transactions.data.pageLinks : null
  122. }
  123. />
  124. </Layout.Main>
  125. </Layout.Body>
  126. </StyledPageContent>
  127. </NoProjectMessage>
  128. </PageFiltersContainer>
  129. </SentryDocumentTitle>
  130. );
  131. }
  132. const StyledPageContent = styled(PageContent)`
  133. padding: 0;
  134. `;
  135. const StyledLayoutHeaderContent = styled(Layout.HeaderContent)`
  136. display: flex;
  137. justify-content: space-between;
  138. flex-direction: row;
  139. `;
  140. const StyledHeading = styled(PageHeading)`
  141. line-height: 40px;
  142. `;
  143. const ActionBar = styled('div')`
  144. display: grid;
  145. gap: ${space(2)};
  146. grid-template-columns: min-content auto;
  147. margin-bottom: ${space(2)};
  148. `;
  149. export default ProfilingContent;