utils.tsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. import {Fragment} from 'react';
  2. import capitalize from 'lodash/capitalize';
  3. import {Alert} from 'sentry/components/alert';
  4. import ExternalLink from 'sentry/components/links/externalLink';
  5. import {t, tct, tn} from 'sentry/locale';
  6. import {IgnoredStatusDetails, Organization} from 'sentry/types';
  7. import ExtraDescription from './extraDescription';
  8. export const BULK_LIMIT = 1000;
  9. export const BULK_LIMIT_STR = BULK_LIMIT.toLocaleString();
  10. export enum ConfirmAction {
  11. RESOLVE = 'resolve',
  12. UNRESOLVE = 'unresolve',
  13. IGNORE = 'ignore',
  14. BOOKMARK = 'bookmark',
  15. UNBOOKMARK = 'unbookmark',
  16. MERGE = 'merge',
  17. DELETE = 'delete',
  18. }
  19. function getBulkConfirmMessage(action: string, queryCount: number) {
  20. if (queryCount > BULK_LIMIT) {
  21. return tct(
  22. 'Are you sure you want to [action] the first [bulkNumber] issues that match the search?',
  23. {
  24. action,
  25. bulkNumber: BULK_LIMIT_STR,
  26. }
  27. );
  28. }
  29. return tct(
  30. 'Are you sure you want to [action] all [bulkNumber] issues that match the search?',
  31. {
  32. action,
  33. bulkNumber: queryCount,
  34. }
  35. );
  36. }
  37. function PerformanceIssueAlert({
  38. allInQuerySelected,
  39. children,
  40. }: {
  41. allInQuerySelected: boolean;
  42. children: string;
  43. }) {
  44. if (!allInQuerySelected) {
  45. return null;
  46. }
  47. return (
  48. <Alert type="info" showIcon>
  49. {children}
  50. </Alert>
  51. );
  52. }
  53. export function getConfirm({
  54. numIssues,
  55. allInQuerySelected,
  56. query,
  57. queryCount,
  58. organization,
  59. }: {
  60. allInQuerySelected: boolean;
  61. numIssues: number;
  62. organization: Organization;
  63. query: string;
  64. queryCount: number;
  65. }) {
  66. return function ({
  67. action,
  68. canBeUndone,
  69. append = '',
  70. }: {
  71. action: ConfirmAction;
  72. canBeUndone: boolean;
  73. append?: string;
  74. }) {
  75. const actionText =
  76. action === ConfirmAction.IGNORE &&
  77. organization.features.includes('escalating-issues')
  78. ? t('archive')
  79. : action;
  80. const question = allInQuerySelected
  81. ? getBulkConfirmMessage(`${actionText}${append}`, queryCount)
  82. : tn(
  83. // Use sprintf argument swapping since the number value must come
  84. // first. See https://github.com/alexei/sprintf.js#argument-swapping
  85. `Are you sure you want to %2$s this %s issue%3$s?`,
  86. `Are you sure you want to %2$s these %s issues%3$s?`,
  87. numIssues,
  88. actionText,
  89. append
  90. );
  91. let message: React.ReactNode;
  92. switch (action) {
  93. case ConfirmAction.DELETE:
  94. message = (
  95. <Fragment>
  96. <p>
  97. {tct(
  98. 'Bulk deletion is only recommended for junk data. To clear your stream, consider resolving or ignoring. [link:When should I delete events?]',
  99. {
  100. link: (
  101. <ExternalLink href="https://help.sentry.io/account/billing/when-should-i-delete-events/" />
  102. ),
  103. }
  104. )}
  105. </p>
  106. <PerformanceIssueAlert allInQuerySelected={allInQuerySelected}>
  107. {t('Deleting performance issues is not yet supported and will be skipped.')}
  108. </PerformanceIssueAlert>
  109. </Fragment>
  110. );
  111. break;
  112. case ConfirmAction.MERGE:
  113. message = (
  114. <Fragment>
  115. <p>{t('Note that unmerging is currently an experimental feature.')}</p>
  116. <PerformanceIssueAlert allInQuerySelected={allInQuerySelected}>
  117. {t('Merging performance issues is not yet supported and will be skipped.')}
  118. </PerformanceIssueAlert>
  119. </Fragment>
  120. );
  121. break;
  122. default:
  123. message = !canBeUndone ? <p>{t('This action cannot be undone.')}</p> : null;
  124. }
  125. return (
  126. <div>
  127. <p style={{marginBottom: '20px'}}>
  128. <strong>{question}</strong>
  129. </p>
  130. <ExtraDescription
  131. all={allInQuerySelected}
  132. query={query}
  133. queryCount={queryCount}
  134. />
  135. {message}
  136. </div>
  137. );
  138. };
  139. }
  140. export function getLabel(numIssues: number, allInQuerySelected: boolean) {
  141. return function (action: string, append = '') {
  142. const capitalized = capitalize(action);
  143. const text = allInQuerySelected
  144. ? t('Bulk %s issues', action)
  145. : // Use sprintf argument swapping to put the capitalized string first. See
  146. // https://github.com/alexei/sprintf.js#argument-swapping
  147. tn(`%2$s %s selected issue`, `%2$s %s selected issues`, numIssues, capitalized);
  148. return text + append;
  149. };
  150. }
  151. export function performanceIssuesSupportsIgnoreAction(
  152. statusDetails: IgnoredStatusDetails
  153. ) {
  154. return !(statusDetails.ignoreWindow || statusDetails.ignoreUserWindow);
  155. }