eventOrGroupExtraDetails.tsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. import styled from '@emotion/styled';
  2. import EventAnnotation from 'sentry/components/events/eventAnnotation';
  3. import GlobalSelectionLink from 'sentry/components/globalSelectionLink';
  4. import InboxReason from 'sentry/components/group/inboxBadges/inboxReason';
  5. import InboxShortId from 'sentry/components/group/inboxBadges/shortId';
  6. import TimesTag from 'sentry/components/group/inboxBadges/timesTag';
  7. import UnhandledTag from 'sentry/components/group/inboxBadges/unhandledTag';
  8. import IssueReplayCount from 'sentry/components/group/issueReplayCount';
  9. import ProjectBadge from 'sentry/components/idBadge/projectBadge';
  10. import Link from 'sentry/components/links/link';
  11. import Placeholder from 'sentry/components/placeholder';
  12. import {IconChat} from 'sentry/icons';
  13. import {tct} from 'sentry/locale';
  14. import {space} from 'sentry/styles/space';
  15. import {Group, Organization} from 'sentry/types';
  16. import {Event} from 'sentry/types/event';
  17. import withOrganization from 'sentry/utils/withOrganization';
  18. type Props = {
  19. data: Event | Group;
  20. organization: Organization;
  21. showAssignee?: boolean;
  22. showInboxTime?: boolean;
  23. };
  24. function EventOrGroupExtraDetails({
  25. data,
  26. showAssignee,
  27. showInboxTime,
  28. organization,
  29. }: Props) {
  30. const {
  31. id,
  32. lastSeen,
  33. firstSeen,
  34. subscriptionDetails,
  35. numComments,
  36. logger,
  37. assignedTo,
  38. annotations,
  39. shortId,
  40. project,
  41. lifetime,
  42. isUnhandled,
  43. inbox,
  44. status,
  45. substatus,
  46. } = data as Group;
  47. const issuesPath = `/organizations/${organization.slug}/issues/`;
  48. const showReplayCount = organization.features.includes('session-replay');
  49. const hasEscalatingIssues = organization.features.includes('escalating-issues-ui');
  50. return (
  51. <GroupExtra>
  52. {inbox && <InboxReason inbox={inbox} showDateAdded={showInboxTime} />}
  53. {hasEscalatingIssues && status === 'ignored' && (
  54. <InboxReason
  55. inbox={{
  56. reason: 'archived',
  57. reason_details: {substatus},
  58. }}
  59. showDateAdded={false}
  60. />
  61. )}
  62. {shortId && (
  63. <InboxShortId
  64. shortId={shortId}
  65. avatar={
  66. project && (
  67. <ShadowlessProjectBadge project={project} avatarSize={12} hideName />
  68. )
  69. }
  70. />
  71. )}
  72. {isUnhandled && <UnhandledTag />}
  73. {!lifetime && !firstSeen && !lastSeen ? (
  74. <Placeholder height="14px" width="100px" />
  75. ) : (
  76. <TimesTag
  77. lastSeen={lifetime?.lastSeen || lastSeen}
  78. firstSeen={lifetime?.firstSeen || firstSeen}
  79. />
  80. )}
  81. {/* Always display comment count on inbox */}
  82. {numComments > 0 && (
  83. <CommentsLink to={`${issuesPath}${id}/activity/`} className="comments">
  84. <IconChat
  85. size="xs"
  86. color={
  87. subscriptionDetails?.reason === 'mentioned' ? 'successText' : undefined
  88. }
  89. />
  90. <span>{numComments}</span>
  91. </CommentsLink>
  92. )}
  93. {showReplayCount && <IssueReplayCount groupId={id} />}
  94. {logger && (
  95. <LoggerAnnotation>
  96. <GlobalSelectionLink
  97. to={{
  98. pathname: issuesPath,
  99. query: {
  100. query: `logger:${logger}`,
  101. },
  102. }}
  103. >
  104. {logger}
  105. </GlobalSelectionLink>
  106. </LoggerAnnotation>
  107. )}
  108. {annotations?.map((annotation, key) => (
  109. <AnnotationNoMargin
  110. dangerouslySetInnerHTML={{
  111. __html: annotation,
  112. }}
  113. key={key}
  114. />
  115. ))}
  116. {showAssignee && assignedTo && (
  117. <div>{tct('Assigned to [name]', {name: assignedTo.name})}</div>
  118. )}
  119. </GroupExtra>
  120. );
  121. }
  122. const GroupExtra = styled('div')`
  123. display: inline-grid;
  124. grid-auto-flow: column dense;
  125. gap: ${space(1.5)};
  126. justify-content: start;
  127. align-items: center;
  128. color: ${p => p.theme.textColor};
  129. font-size: ${p => p.theme.fontSizeSmall};
  130. position: relative;
  131. min-width: 500px;
  132. white-space: nowrap;
  133. line-height: 1.2;
  134. a {
  135. color: inherit;
  136. }
  137. @media (min-width: ${p => p.theme.breakpoints.xlarge}) {
  138. line-height: 1;
  139. }
  140. `;
  141. const ShadowlessProjectBadge = styled(ProjectBadge)`
  142. * > img {
  143. box-shadow: none;
  144. }
  145. `;
  146. const CommentsLink = styled(Link)`
  147. display: inline-grid;
  148. gap: ${space(0.5)};
  149. align-items: center;
  150. grid-auto-flow: column;
  151. color: ${p => p.theme.textColor};
  152. `;
  153. const AnnotationNoMargin = styled(EventAnnotation)`
  154. margin-left: 0;
  155. padding-left: 0;
  156. border-left: none;
  157. & > a {
  158. color: ${p => p.theme.textColor};
  159. }
  160. `;
  161. const LoggerAnnotation = styled(AnnotationNoMargin)`
  162. color: ${p => p.theme.textColor};
  163. `;
  164. export default withOrganization(EventOrGroupExtraDetails);