spanEvidencePreview.tsx 3.6 KB

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