meta.tsx 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import {useMemo} from 'react';
  2. import styled from '@emotion/styled';
  3. import {SectionHeading} from 'sentry/components/charts/styles';
  4. import TimeSince from 'sentry/components/timeSince';
  5. import {t} from 'sentry/locale';
  6. import {space} from 'sentry/styles/space';
  7. import type {EventTransaction} from 'sentry/types/event';
  8. import type {Organization} from 'sentry/types/organization';
  9. import getDuration from 'sentry/utils/duration/getDuration';
  10. import type {
  11. TraceErrorOrIssue,
  12. TraceMeta,
  13. } from 'sentry/utils/performance/quickTrace/types';
  14. import type {UseApiQueryResult} from 'sentry/utils/queryClient';
  15. import type RequestError from 'sentry/utils/requestError/requestError';
  16. import {TraceDrawerComponents} from '../traceDrawer/details/styles';
  17. import type {TraceTree} from '../traceModels/traceTree';
  18. type MetaDataProps = {
  19. bodyText: React.ReactNode;
  20. headingText: string;
  21. rightAlignBody?: boolean;
  22. };
  23. function MetaSection({headingText, bodyText, rightAlignBody}: MetaDataProps) {
  24. return (
  25. <HeaderInfo>
  26. <StyledSectionHeading>{headingText}</StyledSectionHeading>
  27. <SectionBody rightAlign={rightAlignBody}>{bodyText}</SectionBody>
  28. </HeaderInfo>
  29. );
  30. }
  31. const HeaderInfo = styled('div')`
  32. white-space: nowrap;
  33. `;
  34. const StyledSectionHeading = styled(SectionHeading)`
  35. font-size: ${p => p.theme.fontSizeSmall};
  36. margin: 0;
  37. `;
  38. const SectionBody = styled('div')<{rightAlign?: boolean}>`
  39. font-size: ${p => p.theme.fontSizeExtraLarge};
  40. text-align: ${p => (p.rightAlign ? 'right' : 'left')};
  41. padding: ${space(0.5)} 0;
  42. max-height: 32px;
  43. `;
  44. interface MetaProps {
  45. meta: TraceMeta | undefined;
  46. organization: Organization;
  47. representativeTransaction: TraceTree.Transaction | null;
  48. rootEventResults: UseApiQueryResult<EventTransaction, RequestError>;
  49. tree: TraceTree;
  50. }
  51. export function Meta(props: MetaProps) {
  52. const traceNode = props.tree.root.children[0]!;
  53. const uniqueErrorIssues = useMemo(() => {
  54. if (!traceNode) {
  55. return [];
  56. }
  57. const unique: TraceErrorOrIssue[] = [];
  58. const seenIssues: Set<number> = new Set();
  59. for (const issue of traceNode.errors) {
  60. if (seenIssues.has(issue.issue_id)) {
  61. continue;
  62. }
  63. seenIssues.add(issue.issue_id);
  64. unique.push(issue);
  65. }
  66. return unique;
  67. }, [traceNode]);
  68. const uniquePerformanceIssues = useMemo(() => {
  69. if (!traceNode) {
  70. return [];
  71. }
  72. const unique: TraceErrorOrIssue[] = [];
  73. const seenIssues: Set<number> = new Set();
  74. for (const issue of traceNode.performance_issues) {
  75. if (seenIssues.has(issue.issue_id)) {
  76. continue;
  77. }
  78. seenIssues.add(issue.issue_id);
  79. unique.push(issue);
  80. }
  81. return unique;
  82. }, [traceNode]);
  83. const uniqueIssuesCount = uniqueErrorIssues.length + uniquePerformanceIssues.length;
  84. return (
  85. <MetaWrapper>
  86. <MetaSection
  87. headingText={t('Issues')}
  88. bodyText={
  89. uniqueIssuesCount > 0 ? (
  90. <TraceDrawerComponents.IssuesLink node={traceNode}>
  91. {uniqueIssuesCount}
  92. </TraceDrawerComponents.IssuesLink>
  93. ) : uniqueIssuesCount === 0 ? (
  94. 0
  95. ) : (
  96. '\u2014'
  97. )
  98. }
  99. />
  100. <MetaSection
  101. headingText={t('Events')}
  102. bodyText={(props.meta?.transactions ?? 0) + (props.meta?.errors ?? 0)}
  103. />
  104. {traceNode ? (
  105. <MetaSection
  106. headingText={t('Age')}
  107. bodyText={
  108. <TimeSince
  109. unitStyle="extraShort"
  110. date={new Date(traceNode.space[0])}
  111. tooltipShowSeconds
  112. suffix=""
  113. />
  114. }
  115. />
  116. ) : null}
  117. {traceNode ? (
  118. <MetaSection
  119. headingText={t('Root Duration')}
  120. rightAlignBody
  121. bodyText={
  122. props.representativeTransaction
  123. ? getDuration(
  124. props.representativeTransaction.timestamp -
  125. props.representativeTransaction.start_timestamp,
  126. 2,
  127. true
  128. )
  129. : '\u2014'
  130. }
  131. />
  132. ) : null}
  133. </MetaWrapper>
  134. );
  135. }
  136. const MetaWrapper = styled('div')`
  137. display: flex;
  138. align-items: center;
  139. gap: ${space(2)};
  140. ${HeaderInfo} {
  141. min-height: 0;
  142. }
  143. ${SectionBody} {
  144. padding: 0;
  145. }
  146. `;