inboxReason.tsx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. import {Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import DateTime from 'sentry/components/dateTime';
  4. import Tag from 'sentry/components/tag';
  5. import TimeSince, {getRelativeDate} from 'sentry/components/timeSince';
  6. import {t, tct} from 'sentry/locale';
  7. import {InboxDetails} from 'sentry/types';
  8. import {getDuration} from 'sentry/utils/formatters';
  9. import getDynamicText from 'sentry/utils/getDynamicText';
  10. import {Theme} from 'sentry/utils/theme';
  11. const GroupInboxReason = {
  12. NEW: 0,
  13. UNIGNORED: 1,
  14. REGRESSION: 2,
  15. MANUAL: 3,
  16. REPROCESSED: 4,
  17. };
  18. type Props = {
  19. inbox: InboxDetails;
  20. fontSize?: 'sm' | 'md';
  21. /** Displays the time an issue was added to inbox */
  22. showDateAdded?: boolean;
  23. };
  24. const EVENT_ROUND_LIMIT = 1000;
  25. function InboxReason({inbox, fontSize = 'sm', showDateAdded}: Props) {
  26. const {reason, reason_details: reasonDetails, date_added: dateAdded} = inbox;
  27. const relativeDateAdded = getDynamicText({
  28. value: dateAdded && getRelativeDate(dateAdded, 'ago', true),
  29. fixed: '3s ago',
  30. });
  31. const getCountText = (count: number) =>
  32. count > EVENT_ROUND_LIMIT
  33. ? `More than ${Math.round(count / EVENT_ROUND_LIMIT)}k`
  34. : `${count}`;
  35. function getTooltipDescription() {
  36. const {
  37. until,
  38. count,
  39. window,
  40. user_count: userCount,
  41. user_window: userWindow,
  42. } = reasonDetails;
  43. if (until) {
  44. // Was ignored until `until` has passed.
  45. // `until` format: "2021-01-20T03:59:03+00:00"
  46. return tct('Was ignored until [window]', {
  47. window: <DateTime date={until} dateOnly />,
  48. });
  49. }
  50. if (count) {
  51. // Was ignored until `count` events occurred
  52. // If `window` is defined, than `count` events occurred in `window` minutes.
  53. // else `count` events occurred since it was ignored.
  54. if (window) {
  55. return tct('Occurred [count] time(s) in [duration]', {
  56. count: getCountText(count),
  57. duration: getDuration(window * 60, 0, true),
  58. });
  59. }
  60. return tct('Occurred [count] time(s)', {
  61. count: getCountText(count),
  62. });
  63. }
  64. if (userCount) {
  65. // Was ignored until `user_count` users were affected
  66. // If `user_window` is defined, than `user_count` users affected in `user_window` minutes.
  67. // else `user_count` events occurred since it was ignored.
  68. if (userWindow) {
  69. return tct('Affected [count] user(s) in [duration]', {
  70. count: getCountText(userCount),
  71. duration: getDuration(userWindow * 60, 0, true),
  72. });
  73. }
  74. return tct('Affected [count] user(s)', {
  75. count: getCountText(userCount),
  76. });
  77. }
  78. return undefined;
  79. }
  80. function getReasonDetails(): {
  81. reasonBadgeText: string;
  82. tagType: React.ComponentProps<typeof Tag>['type'];
  83. tooltipDescription?: string | React.ReactNode;
  84. tooltipText?: string;
  85. } {
  86. switch (reason) {
  87. case GroupInboxReason.UNIGNORED:
  88. return {
  89. tagType: 'default',
  90. reasonBadgeText: t('Unignored'),
  91. tooltipText:
  92. dateAdded &&
  93. t('Unignored %(relative)s', {
  94. relative: relativeDateAdded,
  95. }),
  96. tooltipDescription: getTooltipDescription(),
  97. };
  98. case GroupInboxReason.REGRESSION:
  99. return {
  100. tagType: 'error',
  101. reasonBadgeText: t('Regression'),
  102. tooltipText:
  103. dateAdded &&
  104. t('Regressed %(relative)s', {
  105. relative: relativeDateAdded,
  106. }),
  107. // TODO: Add tooltip description for regression move when resolver is added to reason
  108. // Resolved by {full_name} {time} ago.
  109. };
  110. // TODO: Manual moves will go away, remove this then
  111. case GroupInboxReason.MANUAL:
  112. return {
  113. tagType: 'highlight',
  114. reasonBadgeText: t('Manual'),
  115. tooltipText:
  116. dateAdded && t('Moved %(relative)s', {relative: relativeDateAdded}),
  117. // TODO: IF manual moves stay then add tooltip description for manual move
  118. // Moved to inbox by {full_name}.
  119. };
  120. case GroupInboxReason.REPROCESSED:
  121. return {
  122. tagType: 'info',
  123. reasonBadgeText: t('Reprocessed'),
  124. tooltipText:
  125. dateAdded &&
  126. t('Reprocessed %(relative)s', {
  127. relative: relativeDateAdded,
  128. }),
  129. };
  130. case GroupInboxReason.NEW:
  131. default:
  132. return {
  133. tagType: 'warning',
  134. reasonBadgeText: t('New Issue'),
  135. tooltipText:
  136. dateAdded &&
  137. t('Created %(relative)s', {
  138. relative: relativeDateAdded,
  139. }),
  140. };
  141. }
  142. }
  143. const {tooltipText, tooltipDescription, reasonBadgeText, tagType} = getReasonDetails();
  144. const tooltip = (tooltipText || tooltipDescription) && (
  145. <TooltipWrapper>
  146. {tooltipText && <div>{tooltipText}</div>}
  147. {tooltipDescription && (
  148. <TooltipDescription>{tooltipDescription}</TooltipDescription>
  149. )}
  150. <TooltipDescription>Mark Reviewed to remove this label</TooltipDescription>
  151. </TooltipWrapper>
  152. );
  153. return (
  154. <StyledTag type={tagType} tooltipText={tooltip} fontSize={fontSize}>
  155. {reasonBadgeText}
  156. {showDateAdded && dateAdded && (
  157. <Fragment>
  158. <Separator type={tagType ?? 'default'}>{' | '}</Separator>
  159. <TimeSince date={dateAdded} suffix="" extraShort disabledAbsoluteTooltip />
  160. </Fragment>
  161. )}
  162. </StyledTag>
  163. );
  164. }
  165. export default InboxReason;
  166. const TooltipWrapper = styled('div')`
  167. text-align: left;
  168. `;
  169. const TooltipDescription = styled('div')`
  170. color: ${p => p.theme.subText};
  171. `;
  172. const Separator = styled('span')<{type: keyof Theme['tag']}>`
  173. color: ${p => p.theme.tag[p.type].border};
  174. opacity: 80%;
  175. `;
  176. const StyledTag = styled(Tag, {
  177. shouldForwardProp: p => p !== 'fontSize',
  178. })<{fontSize: 'sm' | 'md'}>`
  179. font-size: ${p =>
  180. p.fontSize === 'sm' ? p.theme.fontSizeSmall : p.theme.fontSizeMedium};
  181. `;