interactionsLandingPage.tsx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. import {browserHistory} from 'react-router';
  2. import styled from '@emotion/styled';
  3. import Breadcrumbs from 'sentry/components/breadcrumbs';
  4. import DatePageFilter from 'sentry/components/datePageFilter';
  5. import FeatureBadge from 'sentry/components/featureBadge';
  6. import SelectControl, {
  7. ControlProps,
  8. } from 'sentry/components/forms/controls/selectControl';
  9. import * as Layout from 'sentry/components/layouts/thirds';
  10. import PageFilterBar from 'sentry/components/organizations/pageFilterBar';
  11. import {ProjectPageFilter} from 'sentry/components/organizations/projectPageFilter';
  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 {normalizeUrl} from 'sentry/utils/withDomainRequired';
  17. import InteractionsTable from 'sentry/views/performance/browser/interactionTable';
  18. import {
  19. BrowserStarfishFields,
  20. useBrowserModuleFilters,
  21. } from 'sentry/views/performance/browser/useBrowserFilters';
  22. import {usePagesQuery} from 'sentry/views/performance/browser/usePageQuery';
  23. import {ModulePageProviders} from 'sentry/views/performance/database/modulePageProviders';
  24. const {COMPONENT, PAGE, SPAN_ACTION} = BrowserStarfishFields;
  25. type Option = {
  26. label: string;
  27. value: string;
  28. };
  29. function InteractionsLandingPage() {
  30. const organization = useOrganization();
  31. const filters = useBrowserModuleFilters();
  32. return (
  33. <ModulePageProviders title={[t('Performance'), t('Interactions')].join(' — ')}>
  34. <Layout.Header>
  35. <Layout.HeaderContent>
  36. <Breadcrumbs
  37. crumbs={[
  38. {
  39. label: 'Performance',
  40. to: normalizeUrl(`/organizations/${organization.slug}/performance/`),
  41. preservePageFilters: true,
  42. },
  43. {
  44. label: 'Interactions',
  45. },
  46. ]}
  47. />
  48. <Layout.Title>
  49. {t('Interactions')}
  50. <FeatureBadge type="alpha" />
  51. </Layout.Title>
  52. </Layout.HeaderContent>
  53. </Layout.Header>
  54. <Layout.Body>
  55. <Layout.Main fullWidth />
  56. <PaddedContainer>
  57. <PageFilterBar condensed>
  58. <ProjectPageFilter />
  59. <DatePageFilter alignDropdown="left" />
  60. </PageFilterBar>
  61. </PaddedContainer>
  62. <div />
  63. <FilterOptionsContainer>
  64. <ComponentSelector value={filters[COMPONENT] || ''} />
  65. <ActionSelector value={filters[SPAN_ACTION] || ''} />
  66. <PageSelector value={filters[PAGE] || ''} />
  67. </FilterOptionsContainer>
  68. <div />
  69. <InteractionsTable />
  70. </Layout.Body>
  71. </ModulePageProviders>
  72. );
  73. }
  74. function ComponentSelector({value}: {value?: string}) {
  75. const location = useLocation();
  76. const options: Option[] = [
  77. {value: '', label: 'All'},
  78. {value: 'downloadButton', label: '<DownloadButton/>'},
  79. {value: 'closeButton', label: '<CloseButton/>'},
  80. ];
  81. return (
  82. <SelectControlWithProps
  83. inFieldLabel={`${t('Component')}:`}
  84. options={options}
  85. value={value}
  86. onChange={newValue => {
  87. browserHistory.push({
  88. ...location,
  89. query: {
  90. ...location.query,
  91. [COMPONENT]: newValue?.value,
  92. },
  93. });
  94. }}
  95. />
  96. );
  97. }
  98. function ActionSelector({value}: {value?: string}) {
  99. const location = useLocation();
  100. const options: Option[] = [
  101. {value: '', label: 'All'},
  102. {value: 'click', label: 'Click'},
  103. {value: 'change', label: 'Change'},
  104. ];
  105. return (
  106. <SelectControlWithProps
  107. inFieldLabel={`${t('Action')}:`}
  108. options={options}
  109. value={value}
  110. onChange={newValue => {
  111. browserHistory.push({
  112. ...location,
  113. query: {
  114. ...location.query,
  115. [SPAN_ACTION]: newValue?.value,
  116. },
  117. });
  118. }}
  119. />
  120. );
  121. }
  122. function PageSelector({value}: {value?: string}) {
  123. const location = useLocation();
  124. const {data: pages, isLoading} = usePagesQuery();
  125. const options: Option[] =
  126. !isLoading && pages.length
  127. ? [
  128. {label: 'All', value: ''},
  129. ...pages.map(page => ({
  130. value: page,
  131. label: page,
  132. })),
  133. ]
  134. : [];
  135. return (
  136. <SelectControlWithProps
  137. inFieldLabel={`${t('Page')}:`}
  138. options={options}
  139. value={value}
  140. onChange={newValue => {
  141. browserHistory.push({
  142. ...location,
  143. query: {
  144. ...location.query,
  145. [PAGE]: newValue?.value,
  146. },
  147. });
  148. }}
  149. />
  150. );
  151. }
  152. function SelectControlWithProps(props: ControlProps & {options: Option[]}) {
  153. return <SelectControl {...props} />;
  154. }
  155. const PaddedContainer = styled('div')`
  156. margin-bottom: ${space(2)};
  157. `;
  158. const FilterOptionsContainer = styled('div')`
  159. display: grid;
  160. grid-template-columns: repeat(3, 1fr);
  161. gap: ${space(2)};
  162. margin-bottom: ${space(2)};
  163. max-width: 800px;
  164. `;
  165. export default InteractionsLandingPage;