interactionsLandingPage.tsx 5.5 KB

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