eventOrGroupExtraDetails.tsx 4.4 KB

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