webVitalsLandingPage.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import {useMemo, useState} from 'react';
  2. import styled from '@emotion/styled';
  3. import ProjectAvatar from 'sentry/components/avatar/projectAvatar';
  4. import Breadcrumbs from 'sentry/components/breadcrumbs';
  5. import {LinkButton} from 'sentry/components/button';
  6. import FeatureBadge from 'sentry/components/featureBadge';
  7. import * as Layout from 'sentry/components/layouts/thirds';
  8. import {DatePageFilter} from 'sentry/components/organizations/datePageFilter';
  9. import PageFilterBar from 'sentry/components/organizations/pageFilterBar';
  10. import {ProjectPageFilter} from 'sentry/components/organizations/projectPageFilter';
  11. import {IconChevron} from 'sentry/icons';
  12. import {t} from 'sentry/locale';
  13. import {space} from 'sentry/styles/space';
  14. import {useLocation} from 'sentry/utils/useLocation';
  15. import useOrganization from 'sentry/utils/useOrganization';
  16. import useProjects from 'sentry/utils/useProjects';
  17. import {normalizeUrl} from 'sentry/utils/withDomainRequired';
  18. import WebVitalMeters from 'sentry/views/performance/browser/webVitals/components/webVitalMeters';
  19. import {PagePerformanceTable} from 'sentry/views/performance/browser/webVitals/pagePerformanceTable';
  20. import {PageSamplePerformanceTable} from 'sentry/views/performance/browser/webVitals/pageSamplePerformanceTable';
  21. import {PerformanceScoreChart} from 'sentry/views/performance/browser/webVitals/performanceScoreChart';
  22. import {calculatePerformanceScore} from 'sentry/views/performance/browser/webVitals/utils/calculatePerformanceScore';
  23. import {WebVitals} from 'sentry/views/performance/browser/webVitals/utils/types';
  24. import {useProjectWebVitalsQuery} from 'sentry/views/performance/browser/webVitals/utils/useProjectWebVitalsQuery';
  25. import {WebVitalsDetailPanel} from 'sentry/views/performance/browser/webVitals/webVitalsDetailPanel';
  26. import {ModulePageProviders} from 'sentry/views/performance/database/modulePageProviders';
  27. export default function WebVitalsLandingPage() {
  28. const organization = useOrganization();
  29. const location = useLocation();
  30. const {projects} = useProjects();
  31. const transaction = location.query.transaction
  32. ? Array.isArray(location.query.transaction)
  33. ? location.query.transaction[0]
  34. : location.query.transaction
  35. : undefined;
  36. const project = useMemo(
  37. () => projects.find(p => p.id === String(location.query.project)),
  38. [projects, location.query.project]
  39. );
  40. const [state, setState] = useState<{webVital: WebVitals | null}>({
  41. webVital: null,
  42. });
  43. const {data: projectData, isLoading} = useProjectWebVitalsQuery({transaction});
  44. const projectScore = isLoading
  45. ? undefined
  46. : calculatePerformanceScore({
  47. lcp: projectData?.data[0]['p75(measurements.lcp)'] as number,
  48. fcp: projectData?.data[0]['p75(measurements.fcp)'] as number,
  49. cls: projectData?.data[0]['p75(measurements.cls)'] as number,
  50. ttfb: projectData?.data[0]['p75(measurements.ttfb)'] as number,
  51. fid: projectData?.data[0]['p75(measurements.fid)'] as number,
  52. });
  53. return (
  54. <ModulePageProviders title={[t('Performance'), t('Web Vitals')].join(' — ')}>
  55. <Layout.Header>
  56. <Layout.HeaderContent>
  57. <Breadcrumbs
  58. crumbs={[
  59. {
  60. label: 'Performance',
  61. to: normalizeUrl(`/organizations/${organization.slug}/performance/`),
  62. preservePageFilters: true,
  63. },
  64. {
  65. label: 'Web Vitals',
  66. },
  67. ...(transaction ? [{label: 'Page Overview'}] : []),
  68. ]}
  69. />
  70. <Layout.Title>
  71. {transaction && project && <ProjectAvatar project={project} size={24} />}
  72. {transaction ?? t('Web Vitals')}
  73. <FeatureBadge type="alpha" />
  74. </Layout.Title>
  75. </Layout.HeaderContent>
  76. </Layout.Header>
  77. <Layout.Body>
  78. <Layout.Main fullWidth>
  79. <TopMenuContainer>
  80. {transaction && (
  81. <ViewAllPagesButton
  82. to={{...location, query: {...location.query, transaction: undefined}}}
  83. >
  84. <IconChevron direction="left" /> {t('View All Pages')}
  85. </ViewAllPagesButton>
  86. )}
  87. <PageFilterBar condensed>
  88. <ProjectPageFilter />
  89. <DatePageFilter />
  90. </PageFilterBar>
  91. </TopMenuContainer>
  92. <PerformanceScoreChart projectScore={projectScore} transaction={transaction} />
  93. <WebVitalMeters
  94. projectData={projectData}
  95. projectScore={projectScore}
  96. onClick={webVital => setState({...state, webVital})}
  97. transaction={transaction}
  98. />
  99. {!transaction && <PagePerformanceTable />}
  100. {transaction && <PageSamplePerformanceTable transaction={transaction} />}
  101. </Layout.Main>
  102. </Layout.Body>
  103. <WebVitalsDetailPanel
  104. webVital={state.webVital}
  105. onClose={() => {
  106. setState({...state, webVital: null});
  107. }}
  108. />
  109. </ModulePageProviders>
  110. );
  111. }
  112. const ViewAllPagesButton = styled(LinkButton)`
  113. margin-right: ${space(1)};
  114. `;
  115. const TopMenuContainer = styled('div')`
  116. display: flex;
  117. `;