index.tsx 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. import {useCallback} from 'react';
  2. import styled from '@emotion/styled';
  3. import Breadcrumbs from 'sentry/components/breadcrumbs';
  4. import ButtonBar from 'sentry/components/buttonBar';
  5. import DiscoverButton from 'sentry/components/discoverButton';
  6. import {HighlightsIconSummary} from 'sentry/components/events/highlights/highlightsIconSummary';
  7. import FeedbackWidgetButton from 'sentry/components/feedback/widget/feedbackWidgetButton';
  8. import * as Layout from 'sentry/components/layouts/thirds';
  9. import Placeholder from 'sentry/components/placeholder';
  10. import {t} from 'sentry/locale';
  11. import {space} from 'sentry/styles/space';
  12. import type {EventTransaction} from 'sentry/types/event';
  13. import type {Organization} from 'sentry/types/organization';
  14. import {trackAnalytics} from 'sentry/utils/analytics';
  15. import type EventView from 'sentry/utils/discover/eventView';
  16. import {SavedQueryDatasets} from 'sentry/utils/discover/types';
  17. import type {UseApiQueryResult} from 'sentry/utils/queryClient';
  18. import type RequestError from 'sentry/utils/requestError/requestError';
  19. import {useLocation} from 'sentry/utils/useLocation';
  20. import {hasDatasetSelector} from 'sentry/views/dashboards/utils';
  21. import {ProjectsRenderer} from 'sentry/views/explore/tables/tracesTable/fieldRenderers';
  22. import {useModuleURLBuilder} from 'sentry/views/insights/common/utils/useModuleURL';
  23. import {useDomainViewFilters} from 'sentry/views/insights/pages/useFilters';
  24. import type {TraceMetaQueryResults} from '../traceApi/useTraceMeta';
  25. import TraceConfigurations from '../traceConfigurations';
  26. import type {TraceTree} from '../traceModels/traceTree';
  27. import {useHasTraceNewUi} from '../useHasTraceNewUi';
  28. import {getTraceViewBreadcrumbs} from './breadcrumbs';
  29. import {Meta} from './meta';
  30. import {Title} from './title';
  31. interface TraceMetadataHeaderProps {
  32. metaResults: TraceMetaQueryResults;
  33. organization: Organization;
  34. rootEventResults: UseApiQueryResult<EventTransaction, RequestError>;
  35. traceEventView: EventView;
  36. traceSlug: string;
  37. tree: TraceTree;
  38. }
  39. function PlaceHolder({organization}: {organization: Organization}) {
  40. const {view} = useDomainViewFilters();
  41. const moduleURLBuilder = useModuleURLBuilder(true);
  42. const location = useLocation();
  43. return (
  44. <Layout.Header>
  45. <HeaderContent>
  46. <HeaderRow>
  47. <Breadcrumbs
  48. crumbs={getTraceViewBreadcrumbs(
  49. organization,
  50. location,
  51. moduleURLBuilder,
  52. view
  53. )}
  54. />
  55. </HeaderRow>
  56. <HeaderRow>
  57. <PlaceHolderTitleWrapper>
  58. <StyledPlaceholder _width={300} _height={20} />
  59. <StyledPlaceholder _width={200} _height={18} />
  60. </PlaceHolderTitleWrapper>
  61. <PlaceHolderTitleWrapper>
  62. <StyledPlaceholder _width={300} _height={18} />
  63. <StyledPlaceholder _width={300} _height={24} />
  64. </PlaceHolderTitleWrapper>
  65. </HeaderRow>
  66. <StyledBreak />
  67. <HeaderRow>
  68. <PlaceHolderHighlightWrapper>
  69. <StyledPlaceholder _width={150} _height={20} />
  70. <StyledPlaceholder _width={150} _height={20} />
  71. <StyledPlaceholder _width={150} _height={20} />
  72. </PlaceHolderHighlightWrapper>
  73. <StyledPlaceholder _width={50} _height={28} />
  74. </HeaderRow>
  75. </HeaderContent>
  76. </Layout.Header>
  77. );
  78. }
  79. const PlaceHolderTitleWrapper = styled('div')`
  80. display: flex;
  81. flex-direction: column;
  82. gap: ${space(0.5)};
  83. `;
  84. const PlaceHolderHighlightWrapper = styled('div')`
  85. display: flex;
  86. align-items: center;
  87. gap: ${space(1)};
  88. `;
  89. const StyledPlaceholder = styled(Placeholder)<{_height: number; _width: number}>`
  90. border-radius: ${p => p.theme.borderRadius};
  91. height: ${p => p._height}px;
  92. width: ${p => p._width}px;
  93. `;
  94. function LegacyTraceMetadataHeader(props: TraceMetadataHeaderProps) {
  95. const location = useLocation();
  96. const {view} = useDomainViewFilters();
  97. const moduleURLBuilder = useModuleURLBuilder(true);
  98. const trackOpenInDiscover = useCallback(() => {
  99. trackAnalytics('performance_views.trace_view.open_in_discover', {
  100. organization: props.organization,
  101. });
  102. }, [props.organization]);
  103. return (
  104. <Layout.Header>
  105. <Layout.HeaderContent>
  106. <Breadcrumbs
  107. crumbs={getTraceViewBreadcrumbs(
  108. props.organization,
  109. location,
  110. moduleURLBuilder,
  111. view
  112. )}
  113. />
  114. </Layout.HeaderContent>
  115. <Layout.HeaderActions>
  116. <ButtonBar gap={1}>
  117. <TraceConfigurations rootEventResults={props.rootEventResults} />
  118. <DiscoverButton
  119. size="sm"
  120. to={props.traceEventView.getResultsViewUrlTarget(
  121. props.organization.slug,
  122. false,
  123. hasDatasetSelector(props.organization)
  124. ? SavedQueryDatasets.ERRORS
  125. : undefined
  126. )}
  127. onClick={trackOpenInDiscover}
  128. >
  129. {t('Open in Discover')}
  130. </DiscoverButton>
  131. <FeedbackWidgetButton />
  132. </ButtonBar>
  133. </Layout.HeaderActions>
  134. </Layout.Header>
  135. );
  136. }
  137. export function TraceMetaDataHeader(props: TraceMetadataHeaderProps) {
  138. const location = useLocation();
  139. const hasNewTraceViewUi = useHasTraceNewUi();
  140. const {view} = useDomainViewFilters();
  141. const moduleURLBuilder = useModuleURLBuilder(true);
  142. if (!hasNewTraceViewUi) {
  143. return <LegacyTraceMetadataHeader {...props} />;
  144. }
  145. const isLoading =
  146. props.metaResults.status === 'pending' ||
  147. props.rootEventResults.isPending ||
  148. props.tree.type === 'loading';
  149. if (isLoading) {
  150. return <PlaceHolder organization={props.organization} />;
  151. }
  152. return (
  153. <HeaderLayout>
  154. <HeaderContent>
  155. <HeaderRow>
  156. <Breadcrumbs
  157. crumbs={getTraceViewBreadcrumbs(
  158. props.organization,
  159. location,
  160. moduleURLBuilder,
  161. view
  162. )}
  163. />
  164. </HeaderRow>
  165. <HeaderRow>
  166. <Title traceSlug={props.traceSlug} tree={props.tree} />
  167. <Meta
  168. organization={props.organization}
  169. rootEventResults={props.rootEventResults}
  170. tree={props.tree}
  171. meta={props.metaResults.data}
  172. />
  173. </HeaderRow>
  174. <StyledBreak />
  175. {props.rootEventResults.data ? (
  176. <HeaderRow>
  177. <StyledWrapper>
  178. <HighlightsIconSummary event={props.rootEventResults.data} />
  179. </StyledWrapper>
  180. <ProjectsRenderer
  181. projectSlugs={Array.from(props.tree.projects).map(({slug}) => slug)}
  182. visibleAvatarSize={24}
  183. maxVisibleProjects={3}
  184. />
  185. </HeaderRow>
  186. ) : null}
  187. </HeaderContent>
  188. </HeaderLayout>
  189. );
  190. }
  191. const HeaderLayout = styled(Layout.Header)`
  192. padding: ${space(2)} ${space(2)} !important;
  193. `;
  194. const HeaderRow = styled('div')`
  195. display: flex;
  196. justify-content: space-between;
  197. &:not(:first-child) {
  198. margin: ${space(1)} 0;
  199. }
  200. @media (max-width: ${p => p.theme.breakpoints.small}) {
  201. flex-direction: column;
  202. }
  203. `;
  204. const HeaderContent = styled('div')`
  205. display: flex;
  206. flex-direction: column;
  207. `;
  208. const StyledBreak = styled('hr')`
  209. margin: 0;
  210. border-color: ${p => p.theme.border};
  211. `;
  212. const StyledWrapper = styled('span')`
  213. display: flex;
  214. align-items: center;
  215. & > div {
  216. padding: 0;
  217. }
  218. `;