frontendOverviewPage.tsx 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. import styled from '@emotion/styled';
  2. import Feature from 'sentry/components/acl/feature';
  3. import {COL_WIDTH_UNDEFINED} from 'sentry/components/gridEditable';
  4. import * as Layout from 'sentry/components/layouts/thirds';
  5. import {NoAccess} from 'sentry/components/noAccess';
  6. import {DatePageFilter} from 'sentry/components/organizations/datePageFilter';
  7. import {EnvironmentPageFilter} from 'sentry/components/organizations/environmentPageFilter';
  8. import PageFilterBar from 'sentry/components/organizations/pageFilterBar';
  9. import PageFiltersContainer from 'sentry/components/organizations/pageFilters/container';
  10. import {ProjectPageFilter} from 'sentry/components/organizations/projectPageFilter';
  11. import TransactionNameSearchBar from 'sentry/components/performance/searchBar';
  12. import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
  13. import {trackAnalytics} from 'sentry/utils/analytics';
  14. import {canUseMetricsData} from 'sentry/utils/performance/contexts/metricsEnhancedSetting';
  15. import {PageAlert, usePageAlert} from 'sentry/utils/performance/contexts/pageAlert';
  16. import {PerformanceDisplayProvider} from 'sentry/utils/performance/contexts/performanceDisplayContext';
  17. import {MutableSearch} from 'sentry/utils/tokenizeSearch';
  18. import {useLocation} from 'sentry/utils/useLocation';
  19. import {useNavigate} from 'sentry/utils/useNavigate';
  20. import useOrganization from 'sentry/utils/useOrganization';
  21. import useProjects from 'sentry/utils/useProjects';
  22. import * as ModuleLayout from 'sentry/views/insights/common/components/moduleLayout';
  23. import {ToolRibbon} from 'sentry/views/insights/common/components/ribbon';
  24. import {useOnboardingProject} from 'sentry/views/insights/common/queries/useOnboardingProject';
  25. import {FrontendHeader} from 'sentry/views/insights/pages/frontend/frontendPageHeader';
  26. import {OVERVIEW_PAGE_TITLE} from 'sentry/views/insights/pages/settings';
  27. import {generateFrontendOtherPerformanceEventView} from 'sentry/views/performance/data';
  28. import {
  29. DoubleChartRow,
  30. TripleChartRow,
  31. } from 'sentry/views/performance/landing/widgets/components/widgetChartRow';
  32. import {PerformanceWidgetSetting} from 'sentry/views/performance/landing/widgets/widgetDefinitions';
  33. import Onboarding from 'sentry/views/performance/onboarding';
  34. import Table from 'sentry/views/performance/table';
  35. import {
  36. getTransactionSearchQuery,
  37. ProjectPerformanceType,
  38. } from 'sentry/views/performance/utils';
  39. export const FRONTEND_COLUMN_TITLES = [
  40. 'route',
  41. 'project',
  42. 'operation',
  43. 'tpm',
  44. 'p50()',
  45. 'p75()',
  46. 'p95()',
  47. 'users',
  48. ];
  49. function FrontendOverviewPage() {
  50. const organization = useOrganization();
  51. const location = useLocation();
  52. const {setPageError} = usePageAlert();
  53. const {projects} = useProjects();
  54. const onboardingProject = useOnboardingProject();
  55. const navigate = useNavigate();
  56. const withStaticFilters = canUseMetricsData(organization);
  57. const eventView = generateFrontendOtherPerformanceEventView(
  58. location,
  59. withStaticFilters,
  60. organization
  61. );
  62. // TODO - this should come from MetricsField / EAP fields
  63. eventView.fields = [
  64. {field: 'team_key_transaction'},
  65. {field: 'transaction'},
  66. {field: 'project'},
  67. {field: 'transaction.op'},
  68. {field: 'tpm()'},
  69. {field: 'p50(transaction.duration)'},
  70. {field: 'p75(transaction.duration)'},
  71. {field: 'p95(transaction.duration)'},
  72. ].map(field => ({...field, width: COL_WIDTH_UNDEFINED}));
  73. const showOnboarding = onboardingProject !== undefined;
  74. const doubleChartRowCharts = [
  75. PerformanceWidgetSetting.SLOW_HTTP_OPS,
  76. PerformanceWidgetSetting.SLOW_RESOURCE_OPS,
  77. ];
  78. const tripleChartRowCharts = [
  79. PerformanceWidgetSetting.TPM_AREA,
  80. PerformanceWidgetSetting.DURATION_HISTOGRAM,
  81. PerformanceWidgetSetting.P50_DURATION_AREA,
  82. PerformanceWidgetSetting.P75_DURATION_AREA,
  83. PerformanceWidgetSetting.P95_DURATION_AREA,
  84. PerformanceWidgetSetting.P99_DURATION_AREA,
  85. PerformanceWidgetSetting.FAILURE_RATE_AREA,
  86. ];
  87. if (organization.features.includes('insights-initial-modules')) {
  88. doubleChartRowCharts.unshift(PerformanceWidgetSetting.MOST_TIME_CONSUMING_DOMAINS);
  89. doubleChartRowCharts.unshift(PerformanceWidgetSetting.MOST_TIME_CONSUMING_RESOURCES);
  90. doubleChartRowCharts.unshift(PerformanceWidgetSetting.HIGHEST_OPPORTUNITY_PAGES);
  91. }
  92. const sharedProps = {eventView, location, organization, withStaticFilters};
  93. const getFreeTextFromQuery = (query: string) => {
  94. const conditions = new MutableSearch(query);
  95. const transactionValues = conditions.getFilterValues('transaction');
  96. if (transactionValues.length) {
  97. return transactionValues[0];
  98. }
  99. if (conditions.freeText.length > 0) {
  100. // raw text query will be wrapped in wildcards in generatePerformanceEventView
  101. // so no need to wrap it here
  102. return conditions.freeText.join(' ');
  103. }
  104. return '';
  105. };
  106. function handleSearch(searchQuery: string) {
  107. trackAnalytics('performance.domains.frontend.search', {organization});
  108. navigate({
  109. pathname: location.pathname,
  110. query: {
  111. ...location.query,
  112. cursor: undefined,
  113. query: String(searchQuery).trim() || undefined,
  114. isDefaultQuery: false,
  115. },
  116. });
  117. }
  118. const derivedQuery = getTransactionSearchQuery(location, eventView.query);
  119. return (
  120. <Feature
  121. features="insights-domain-view"
  122. organization={organization}
  123. renderDisabled={NoAccess}
  124. >
  125. <Layout.Header>
  126. <FrontendHeader />
  127. </Layout.Header>
  128. <Layout.Body>
  129. <Layout.Main fullWidth>
  130. <ModuleLayout.Layout>
  131. <ModuleLayout.Full>
  132. <ToolRibbon>
  133. <PageFilterBar condensed>
  134. <ProjectPageFilter />
  135. <EnvironmentPageFilter />
  136. <DatePageFilter />
  137. </PageFilterBar>
  138. {!showOnboarding && (
  139. <StyledTransactionNameSearchBar
  140. organization={organization}
  141. eventView={eventView}
  142. onSearch={(query: string) => {
  143. handleSearch(query);
  144. }}
  145. query={getFreeTextFromQuery(derivedQuery)}
  146. />
  147. )}
  148. </ToolRibbon>
  149. </ModuleLayout.Full>
  150. <PageAlert />
  151. <ModuleLayout.Full>
  152. {!showOnboarding && (
  153. <PerformanceDisplayProvider
  154. value={{performanceType: ProjectPerformanceType.FRONTEND_OTHER}}
  155. >
  156. <DoubleChartRow allowedCharts={doubleChartRowCharts} {...sharedProps} />
  157. <TripleChartRow allowedCharts={tripleChartRowCharts} {...sharedProps} />
  158. <Table
  159. projects={projects}
  160. columnTitles={FRONTEND_COLUMN_TITLES}
  161. setError={setPageError}
  162. {...sharedProps}
  163. />
  164. </PerformanceDisplayProvider>
  165. )}
  166. {showOnboarding && (
  167. <Onboarding project={onboardingProject} organization={organization} />
  168. )}
  169. </ModuleLayout.Full>
  170. </ModuleLayout.Layout>
  171. </Layout.Main>
  172. </Layout.Body>
  173. </Feature>
  174. );
  175. }
  176. function FrontendOverviewPageWithProviders() {
  177. const organization = useOrganization();
  178. return (
  179. <PageFiltersContainer>
  180. <SentryDocumentTitle title={OVERVIEW_PAGE_TITLE} orgSlug={organization.slug}>
  181. <FrontendOverviewPage />
  182. </SentryDocumentTitle>
  183. </PageFiltersContainer>
  184. );
  185. }
  186. const StyledTransactionNameSearchBar = styled(TransactionNameSearchBar)`
  187. flex: 2;
  188. `;
  189. export default FrontendOverviewPageWithProviders;