traceIssue.tsx 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import {Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import ProjectBadge from 'sentry/components/idBadge/projectBadge';
  4. import Link from 'sentry/components/links/link';
  5. import LoadingIndicator from 'sentry/components/loadingIndicator';
  6. import Placeholder from 'sentry/components/placeholder';
  7. import {space} from 'sentry/styles/space';
  8. import type {Group} from 'sentry/types/group';
  9. import {useApiQuery} from 'sentry/utils/queryClient';
  10. import useOrganization from 'sentry/utils/useOrganization';
  11. import useProjectFromSlug from 'sentry/utils/useProjectFromSlug';
  12. import {getGroupDetailsQueryData} from 'sentry/views/issueDetails/utils';
  13. import type {TimelineEvent} from './useTraceTimelineEvents';
  14. interface TraceIssueEventProps {
  15. event: TimelineEvent;
  16. }
  17. export function TraceIssueEvent({event}: TraceIssueEventProps) {
  18. const organization = useOrganization();
  19. const project = useProjectFromSlug({organization, projectSlug: event['project.name']});
  20. const issueId = event['issue.id'];
  21. // We are view issue X and loading data for issue Y. This means that this call will not be cached,
  22. // thus, it is a little bit slow to render the component.
  23. // XXX: Create new endpoint that only fetches the metadata for the group
  24. const {data: groupData, isLoading: isLoadingGroupData} = useApiQuery<Group>(
  25. [
  26. `/organizations/${organization.slug}/issues/${issueId}/`,
  27. {query: getGroupDetailsQueryData({})},
  28. ],
  29. {
  30. staleTime: 30000,
  31. cacheTime: 30000,
  32. retry: false,
  33. }
  34. );
  35. const avatarSize = parseInt(space(4), 10);
  36. // If any of data fails to load, we don't want to render the component
  37. // Only "One other issue appears in the same trace. View Full Trace (X issues)" would show up
  38. return (
  39. <Fragment>
  40. {isLoadingGroupData ? (
  41. <LoadingIndicator mini />
  42. ) : (
  43. groupData && (
  44. // XXX: Determine plan for analytics
  45. <TraceIssueLinkContainer
  46. to={{
  47. pathname: `/organizations/${organization.slug}/issues/${issueId}/events/${event.id}/`,
  48. query: {
  49. referrer: 'issues_trace_issue',
  50. },
  51. }}
  52. >
  53. <TraceIssueProjectBadge>
  54. {project ? (
  55. <ProjectBadge
  56. project={project}
  57. avatarSize={avatarSize}
  58. hideName
  59. disableLink
  60. />
  61. ) : (
  62. <Placeholder
  63. shape="rect"
  64. width={`${projectBadgeSize}px`}
  65. height={`${projectBadgeSize}px`}
  66. />
  67. )}
  68. </TraceIssueProjectBadge>
  69. <TraceIssueDetailsContainer>
  70. <NoOverflowDiv>
  71. <TraceIssueEventTitle>
  72. {groupData.metadata.title || groupData.metadata.type}
  73. </TraceIssueEventTitle>
  74. <TraceIssueEventTransaction>
  75. {event.transaction}
  76. </TraceIssueEventTransaction>
  77. </NoOverflowDiv>
  78. <NoOverflowDiv>{groupData.metadata.value}</NoOverflowDiv>
  79. </TraceIssueDetailsContainer>
  80. </TraceIssueLinkContainer>
  81. )
  82. )}
  83. </Fragment>
  84. );
  85. }
  86. const TraceIssueLinkContainer = styled(Link)`
  87. display: flex;
  88. gap: ${space(2)};
  89. color: ${p => p.theme.textColor};
  90. padding: ${space(2)} ${space(2)} ${space(2)} ${space(2)};
  91. margin: ${space(1)} 0 ${space(1)} 0;
  92. border: 1px solid ${p => p.theme.border};
  93. border-radius: ${p => p.theme.borderRadius};
  94. font-size: ${p => p.theme.fontSizeMedium};
  95. &:hover {
  96. background-color: ${p => p.theme.surface200};
  97. color: ${p => p.theme.textColor};
  98. }
  99. `;
  100. // This size helps line up the contents of Suspect Commit
  101. // with the project avatar
  102. const projectBadgeSize = 36;
  103. const TraceIssueProjectBadge = styled('div')`
  104. height: ${projectBadgeSize}px;
  105. width: ${projectBadgeSize}px;
  106. min-width: ${projectBadgeSize}px;
  107. display: flex;
  108. align-self: center;
  109. justify-content: center;
  110. `;
  111. const TraceIssueDetailsContainer = styled('div')`
  112. ${p => p.theme.overflowEllipsis};
  113. `;
  114. const NoOverflowDiv = styled('div')`
  115. ${p => p.theme.overflowEllipsis};
  116. `;
  117. const TraceIssueEventTitle = styled('span')`
  118. font-weight: 600;
  119. margin-right: ${space(1)};
  120. `;
  121. const TraceIssueEventTransaction = styled('span')`
  122. color: ${p => p.theme.subText};
  123. `;