spanEvidencePreview.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. import {Fragment, ReactChild, useEffect} from 'react';
  2. import styled from '@emotion/styled';
  3. import {SpanEvidenceKeyValueList} from 'sentry/components/events/interfaces/performance/spanEvidenceKeyValueList';
  4. import {getSpanInfoFromTransactionEvent} from 'sentry/components/events/interfaces/performance/utils';
  5. import {GroupPreviewHovercard} from 'sentry/components/groupPreviewTooltip/groupPreviewHovercard';
  6. import {useDelayedLoadingState} from 'sentry/components/groupPreviewTooltip/utils';
  7. import LoadingIndicator from 'sentry/components/loadingIndicator';
  8. import {t} from 'sentry/locale';
  9. import space from 'sentry/styles/space';
  10. import {EventTransaction} from 'sentry/types';
  11. import useApiRequests from 'sentry/utils/useApiRequests';
  12. import useOrganization from 'sentry/utils/useOrganization';
  13. type SpanEvidencePreviewProps = {
  14. children: ReactChild;
  15. eventId?: string;
  16. groupId?: string;
  17. projectSlug?: string;
  18. };
  19. type SpanEvidencePreviewBodyProps = {
  20. endpointUrl: string;
  21. onRequestBegin: () => void;
  22. onRequestEnd: () => void;
  23. onUnmount: () => void;
  24. };
  25. type Response = {
  26. event: EventTransaction;
  27. };
  28. const makeGroupPreviewRequestUrl = ({
  29. orgSlug,
  30. eventId,
  31. groupId,
  32. projectSlug,
  33. }: {
  34. orgSlug: string;
  35. eventId?: string;
  36. groupId?: string;
  37. projectSlug?: string;
  38. }) => {
  39. if (eventId && projectSlug) {
  40. return `/projects/${orgSlug}/${projectSlug}/events/${eventId}/`;
  41. }
  42. if (groupId) {
  43. return `/issues/${groupId}/events/latest/`;
  44. }
  45. return null;
  46. };
  47. const SpanEvidencePreviewBody = ({
  48. endpointUrl,
  49. onRequestBegin,
  50. onRequestEnd,
  51. onUnmount,
  52. }: SpanEvidencePreviewBodyProps) => {
  53. const {data, isLoading, hasError} = useApiRequests<Response>({
  54. endpoints: [
  55. ['event', endpointUrl, {query: {referrer: 'api.issues.preview-performance'}}],
  56. ],
  57. onRequestError: onRequestEnd,
  58. onRequestSuccess: onRequestEnd,
  59. });
  60. useEffect(() => {
  61. onRequestBegin();
  62. return onUnmount;
  63. }, [onRequestBegin, onUnmount]);
  64. if (isLoading) {
  65. return (
  66. <EmptyWrapper>
  67. <LoadingIndicator hideMessage size={32} />
  68. </EmptyWrapper>
  69. );
  70. }
  71. if (hasError) {
  72. return <EmptyWrapper>{t('Failed to load preview')}</EmptyWrapper>;
  73. }
  74. const spanInfo = data.event && getSpanInfoFromTransactionEvent(data.event);
  75. if (spanInfo && data.event) {
  76. return (
  77. <SpanEvidencePreviewWrapper data-test-id="span-evidence-preview-body">
  78. <SpanEvidenceKeyValueList
  79. transactionName={data.event.title}
  80. parentSpan={spanInfo.parentSpan}
  81. repeatingSpan={spanInfo.repeatingSpan}
  82. />
  83. </SpanEvidencePreviewWrapper>
  84. );
  85. }
  86. return (
  87. <EmptyWrapper>
  88. {t('There is no span evidence available for this issue.')}
  89. </EmptyWrapper>
  90. );
  91. };
  92. export const SpanEvidencePreview = ({
  93. children,
  94. groupId,
  95. eventId,
  96. projectSlug,
  97. }: SpanEvidencePreviewProps) => {
  98. const organization = useOrganization();
  99. const endpointUrl = makeGroupPreviewRequestUrl({
  100. groupId,
  101. eventId,
  102. projectSlug,
  103. orgSlug: organization.slug,
  104. });
  105. const {shouldShowLoadingState, onRequestBegin, onRequestEnd, reset} =
  106. useDelayedLoadingState();
  107. if (!endpointUrl) {
  108. return <Fragment>{children}</Fragment>;
  109. }
  110. return (
  111. <GroupPreviewHovercard
  112. hide={!shouldShowLoadingState}
  113. body={
  114. <SpanEvidencePreviewBody
  115. onRequestBegin={onRequestBegin}
  116. onRequestEnd={onRequestEnd}
  117. onUnmount={reset}
  118. endpointUrl={endpointUrl}
  119. />
  120. }
  121. >
  122. {children}
  123. </GroupPreviewHovercard>
  124. );
  125. };
  126. const EmptyWrapper = styled('div')`
  127. color: ${p => p.theme.subText};
  128. padding: ${space(1.5)};
  129. font-size: ${p => p.theme.fontSizeMedium};
  130. display: flex;
  131. align-items: center;
  132. justify-content: center;
  133. min-height: 56px;
  134. `;
  135. const SpanEvidencePreviewWrapper = styled('div')`
  136. width: 700px;
  137. padding: ${space(1.5)} ${space(1.5)} 0 ${space(1.5)};
  138. `;