utils.tsx 4.4 KB

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