index.tsx 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. import styled from '@emotion/styled';
  2. import omit from 'lodash/omit';
  3. import Breadcrumbs from 'sentry/components/breadcrumbs';
  4. import ButtonBar from 'sentry/components/buttonBar';
  5. import ErrorBoundary from 'sentry/components/errorBoundary';
  6. import FeedbackWidgetButton from 'sentry/components/feedback/widget/feedbackWidgetButton';
  7. import * as Layout from 'sentry/components/layouts/thirds';
  8. import {DatePageFilter} from 'sentry/components/organizations/datePageFilter';
  9. import {EnvironmentPageFilter} from 'sentry/components/organizations/environmentPageFilter';
  10. import PageFilterBar from 'sentry/components/organizations/pageFilterBar';
  11. import {t} from 'sentry/locale';
  12. import {space} from 'sentry/styles/space';
  13. import {DurationUnit} from 'sentry/utils/discover/fields';
  14. import {DiscoverDatasets} from 'sentry/utils/discover/types';
  15. import {PageAlert, PageAlertProvider} from 'sentry/utils/performance/contexts/pageAlert';
  16. import {useLocation} from 'sentry/utils/useLocation';
  17. import useOrganization from 'sentry/utils/useOrganization';
  18. import useRouter from 'sentry/utils/useRouter';
  19. import {SpanSamplesPanel} from 'sentry/views/performance/mobile/components/spanSamplesPanel';
  20. import {
  21. ScreenCharts,
  22. YAxis,
  23. } from 'sentry/views/performance/mobile/screenload/screenLoadSpans/charts';
  24. import {ScreenLoadEventSamples} from 'sentry/views/performance/mobile/screenload/screenLoadSpans/eventSamples';
  25. import {MetricsRibbon} from 'sentry/views/performance/mobile/screenload/screenLoadSpans/metricsRibbon';
  26. import {ScreenLoadSpansTable} from 'sentry/views/performance/mobile/screenload/screenLoadSpans/table';
  27. import {
  28. MobileCursors,
  29. MobileSortKeys,
  30. } from 'sentry/views/performance/mobile/screenload/screens/constants';
  31. import {PlatformSelector} from 'sentry/views/performance/mobile/screenload/screens/platformSelector';
  32. import useCrossPlatformProject from 'sentry/views/performance/mobile/useCrossPlatformProject';
  33. import {ModulePageProviders} from 'sentry/views/performance/modulePageProviders';
  34. import {useModuleBreadcrumbs} from 'sentry/views/performance/utils/useModuleBreadcrumbs';
  35. import {
  36. PRIMARY_RELEASE_ALIAS,
  37. ReleaseComparisonSelector,
  38. SECONDARY_RELEASE_ALIAS,
  39. } from 'sentry/views/starfish/components/releaseSelector';
  40. import {ModuleName} from 'sentry/views/starfish/types';
  41. import {QueryParameterNames} from 'sentry/views/starfish/views/queryParameters';
  42. type Query = {
  43. primaryRelease: string;
  44. project: string;
  45. secondaryRelease: string;
  46. spanGroup: string;
  47. transaction: string;
  48. [QueryParameterNames.SPANS_SORT]: string;
  49. spanDescription?: string;
  50. };
  51. function ScreenLoadSpans() {
  52. const location = useLocation<Query>();
  53. const organization = useOrganization();
  54. const router = useRouter();
  55. const {isProjectCrossPlatform} = useCrossPlatformProject();
  56. const crumbs = useModuleBreadcrumbs('screen_load');
  57. const {
  58. spanGroup,
  59. primaryRelease,
  60. secondaryRelease,
  61. transaction: transactionName,
  62. spanDescription,
  63. } = location.query;
  64. return (
  65. <Layout.Page>
  66. <PageAlertProvider>
  67. <Layout.Header>
  68. <Layout.HeaderContent>
  69. <Breadcrumbs
  70. crumbs={[
  71. ...crumbs,
  72. {
  73. label: t('Screen Summary'),
  74. },
  75. ]}
  76. />
  77. <HeaderWrapper>
  78. <Layout.Title>{transactionName}</Layout.Title>
  79. {organization.features.includes('insights-initial-modules') &&
  80. isProjectCrossPlatform && <PlatformSelector />}
  81. </HeaderWrapper>
  82. </Layout.HeaderContent>
  83. <Layout.HeaderActions>
  84. <ButtonBar gap={1}>
  85. <FeedbackWidgetButton />
  86. </ButtonBar>
  87. </Layout.HeaderActions>
  88. </Layout.Header>
  89. <Layout.Body>
  90. <Layout.Main fullWidth>
  91. <PageAlert />
  92. <Container>
  93. <FilterContainer>
  94. <PageFilterBar condensed>
  95. <EnvironmentPageFilter />
  96. <DatePageFilter />
  97. </PageFilterBar>
  98. <ReleaseComparisonSelector />
  99. </FilterContainer>
  100. <MetricsRibbon
  101. dataset={DiscoverDatasets.METRICS}
  102. filters={[
  103. 'event.type:transaction',
  104. 'transaction.op:ui.load',
  105. `transaction:${transactionName}`,
  106. ]}
  107. fields={[
  108. `avg_if(measurements.time_to_initial_display,release,${primaryRelease})`,
  109. `avg_if(measurements.time_to_initial_display,release,${secondaryRelease})`,
  110. `avg_if(measurements.time_to_full_display,release,${primaryRelease})`,
  111. `avg_if(measurements.time_to_full_display,release,${secondaryRelease})`,
  112. 'count()',
  113. ]}
  114. blocks={[
  115. {
  116. unit: DurationUnit.MILLISECOND,
  117. dataKey: `avg_if(measurements.time_to_initial_display,release,${primaryRelease})`,
  118. title: t('Avg TTID (%s)', PRIMARY_RELEASE_ALIAS),
  119. },
  120. {
  121. unit: DurationUnit.MILLISECOND,
  122. dataKey: `avg_if(measurements.time_to_initial_display,release,${secondaryRelease})`,
  123. title: t('Avg TTID (%s)', SECONDARY_RELEASE_ALIAS),
  124. },
  125. {
  126. unit: DurationUnit.MILLISECOND,
  127. dataKey: `avg_if(measurements.time_to_full_display,release,${primaryRelease})`,
  128. title: t('Avg TTFD (%s)', PRIMARY_RELEASE_ALIAS),
  129. },
  130. {
  131. unit: DurationUnit.MILLISECOND,
  132. dataKey: `avg_if(measurements.time_to_full_display,release,${secondaryRelease})`,
  133. title: t('Avg TTFD (%s)', SECONDARY_RELEASE_ALIAS),
  134. },
  135. {
  136. unit: 'count',
  137. dataKey: 'count()',
  138. title: t('Total Count'),
  139. },
  140. ]}
  141. referrer="api.starfish.mobile-screen-totals"
  142. />
  143. </Container>
  144. <ErrorBoundary mini>
  145. <ScreenCharts
  146. yAxes={[YAxis.TTID, YAxis.TTFD, YAxis.COUNT]}
  147. additionalFilters={[`transaction:${transactionName}`]}
  148. chartHeight={120}
  149. />
  150. <SampleContainer>
  151. <SampleContainerItem>
  152. <ScreenLoadEventSamples
  153. release={primaryRelease}
  154. sortKey={MobileSortKeys.RELEASE_1_EVENT_SAMPLE_TABLE}
  155. cursorName={MobileCursors.RELEASE_1_EVENT_SAMPLE_TABLE}
  156. transaction={transactionName}
  157. showDeviceClassSelector
  158. />
  159. </SampleContainerItem>
  160. <SampleContainerItem>
  161. <ScreenLoadEventSamples
  162. release={secondaryRelease}
  163. sortKey={MobileSortKeys.RELEASE_2_EVENT_SAMPLE_TABLE}
  164. cursorName={MobileCursors.RELEASE_2_EVENT_SAMPLE_TABLE}
  165. transaction={transactionName}
  166. />
  167. </SampleContainerItem>
  168. </SampleContainer>
  169. <ScreenLoadSpansTable
  170. transaction={transactionName}
  171. primaryRelease={primaryRelease}
  172. secondaryRelease={secondaryRelease}
  173. />
  174. {spanGroup && (
  175. <SpanSamplesPanel
  176. groupId={spanGroup}
  177. moduleName={ModuleName.SCREEN_LOAD}
  178. transactionName={transactionName}
  179. spanDescription={spanDescription}
  180. onClose={() => {
  181. router.replace({
  182. pathname: router.location.pathname,
  183. query: omit(
  184. router.location.query,
  185. 'spanGroup',
  186. 'transactionMethod'
  187. ),
  188. });
  189. }}
  190. />
  191. )}
  192. </ErrorBoundary>
  193. </Layout.Main>
  194. </Layout.Body>
  195. </PageAlertProvider>
  196. </Layout.Page>
  197. );
  198. }
  199. function PageWithProviders() {
  200. const location = useLocation<Query>();
  201. const {transaction} = location.query;
  202. return (
  203. <ModulePageProviders
  204. moduleName="screen_load"
  205. pageTitle={transaction}
  206. features="insights-initial-modules"
  207. >
  208. <ScreenLoadSpans />
  209. </ModulePageProviders>
  210. );
  211. }
  212. export default PageWithProviders;
  213. const Container = styled('div')`
  214. display: grid;
  215. grid-template-rows: 1fr 1fr;
  216. grid-template-columns: 1fr;
  217. column-gap: ${space(2)};
  218. @media (min-width: ${p => p.theme.breakpoints.large}) {
  219. grid-template-rows: auto;
  220. grid-template-columns: auto minmax(100px, max-content);
  221. }
  222. `;
  223. const FilterContainer = styled('div')`
  224. display: grid;
  225. column-gap: ${space(1)};
  226. grid-template-rows: auto;
  227. grid-template-columns: auto 1fr;
  228. `;
  229. const SampleContainer = styled('div')`
  230. display: flex;
  231. flex-direction: row;
  232. flex-wrap: wrap;
  233. gap: ${space(2)};
  234. `;
  235. const SampleContainerItem = styled('div')`
  236. flex: 1;
  237. `;
  238. const HeaderWrapper = styled('div')`
  239. display: flex;
  240. `;