traceHeader.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. import {Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import GuideAnchor from 'sentry/components/assistant/guideAnchor';
  4. import Link from 'sentry/components/links/link';
  5. import LoadingIndicator from 'sentry/components/loadingIndicator';
  6. import {Tooltip} from 'sentry/components/tooltip';
  7. import {IconPlay} from 'sentry/icons';
  8. import {t, tn} from 'sentry/locale';
  9. import {space} from 'sentry/styles/space';
  10. import type {EventTransaction, Organization} from 'sentry/types';
  11. import {getShortEventId} from 'sentry/utils/events';
  12. import {getDuration} from 'sentry/utils/formatters';
  13. import type {TraceMetaQueryChildrenProps} from 'sentry/utils/performance/quickTrace/traceMetaQuery';
  14. import type {
  15. TraceFullDetailed,
  16. TraceSplitResults,
  17. } from 'sentry/utils/performance/quickTrace/types';
  18. import type {UseApiQueryResult} from 'sentry/utils/queryClient';
  19. import type RequestError from 'sentry/utils/requestError/requestError';
  20. import {normalizeUrl} from 'sentry/utils/withDomainRequired';
  21. import {getTraceInfo} from '../traceDetails/utils';
  22. import {BrowserDisplay} from '../transactionDetails/eventMetas';
  23. import {MetaData} from '../transactionDetails/styles';
  24. type TraceHeaderProps = {
  25. metaResults: TraceMetaQueryChildrenProps;
  26. organization: Organization;
  27. rootEventResults: UseApiQueryResult<EventTransaction, RequestError>;
  28. traces: TraceSplitResults<TraceFullDetailed> | null;
  29. };
  30. export default function TraceHeader(props: TraceHeaderProps) {
  31. const {metaResults, rootEventResults, traces, organization} = props;
  32. const {meta, isLoading: metaLoading} = metaResults;
  33. const {data: rootEvent, isLoading: rootEventLoading} = rootEventResults;
  34. const errors = meta?.errors || 0;
  35. const performanceIssues = meta?.performance_issues || 0;
  36. const replay_id = rootEvent?.contexts.replay?.replay_id ?? '';
  37. const traceInfo = getTraceInfo(traces?.transactions, traces?.orphan_errors);
  38. const loadingIndicator = <LoadingIndicator size={20} mini />;
  39. return (
  40. <TraceHeaderContainer>
  41. <TraceHeaderRow>
  42. <MetaData
  43. headingText={t('User')}
  44. tooltipText=""
  45. bodyText={
  46. rootEventLoading
  47. ? loadingIndicator
  48. : rootEvent?.user?.email ?? rootEvent?.user?.name ?? '\u2014'
  49. }
  50. subtext={null}
  51. />
  52. <MetaData
  53. headingText={t('Browser')}
  54. tooltipText=""
  55. bodyText={
  56. rootEventLoading ? (
  57. loadingIndicator
  58. ) : rootEvent ? (
  59. <BrowserDisplay event={rootEvent} showVersion />
  60. ) : (
  61. '\u2014'
  62. )
  63. }
  64. subtext={null}
  65. />
  66. {replay_id && (
  67. <MetaData
  68. headingText={t('Replay')}
  69. tooltipText=""
  70. bodyText={
  71. <Link
  72. to={normalizeUrl(
  73. `/organizations/${organization.slug}/replays/${replay_id}/`
  74. )}
  75. >
  76. <ReplayLinkBody>
  77. {getShortEventId(replay_id)}
  78. <IconPlay size="xs" />
  79. </ReplayLinkBody>
  80. </Link>
  81. }
  82. subtext={null}
  83. />
  84. )}
  85. </TraceHeaderRow>
  86. <TraceHeaderRow>
  87. <GuideAnchor target="trace_view_guide_breakdown">
  88. <MetaData
  89. headingText={t('Events')}
  90. tooltipText=""
  91. bodyText={metaLoading ? loadingIndicator : meta?.transactions ?? '\u2014'}
  92. subtext={null}
  93. />
  94. </GuideAnchor>
  95. <MetaData
  96. headingText={t('Issues')}
  97. tooltipText=""
  98. bodyText={
  99. <Tooltip
  100. title={
  101. errors + performanceIssues > 0 ? (
  102. <Fragment>
  103. <div>{tn('%s error issue', '%s error issues', errors)}</div>
  104. <div>
  105. {tn(
  106. '%s performance issue',
  107. '%s performance issues',
  108. performanceIssues
  109. )}
  110. </div>
  111. </Fragment>
  112. ) : null
  113. }
  114. showUnderline
  115. position="bottom"
  116. >
  117. {metaLoading
  118. ? loadingIndicator
  119. : errors || performanceIssues
  120. ? errors + performanceIssues
  121. : 0}
  122. </Tooltip>
  123. }
  124. subtext={null}
  125. />
  126. <MetaData
  127. headingText={t('Total Duration')}
  128. tooltipText=""
  129. bodyText={
  130. traceInfo.startTimestamp && traceInfo.endTimestamp
  131. ? getDuration(traceInfo.endTimestamp - traceInfo.startTimestamp, 2, true)
  132. : loadingIndicator
  133. }
  134. subtext={null}
  135. />
  136. </TraceHeaderRow>
  137. </TraceHeaderContainer>
  138. );
  139. }
  140. const FlexBox = styled('div')`
  141. display: flex;
  142. align-items: center;
  143. `;
  144. const TraceHeaderContainer = styled(FlexBox)`
  145. justify-content: space-between;
  146. `;
  147. const TraceHeaderRow = styled(FlexBox)`
  148. gap: ${space(2)};
  149. `;
  150. const ReplayLinkBody = styled(FlexBox)`
  151. gap: ${space(0.25)};
  152. `;