utils.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. import React 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 {Organization, ResolutionStatusDetails} 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. organization,
  40. children,
  41. }: {
  42. allInQuerySelected: boolean;
  43. children: string;
  44. organization: Organization;
  45. }) {
  46. if (!allInQuerySelected || !organization.features.includes('performance-issues')) {
  47. return null;
  48. }
  49. return (
  50. <Alert type="info" showIcon>
  51. {children}
  52. </Alert>
  53. );
  54. }
  55. export function getConfirm({
  56. numIssues,
  57. allInQuerySelected,
  58. query,
  59. queryCount,
  60. organization,
  61. }: {
  62. allInQuerySelected: boolean;
  63. numIssues: number;
  64. organization: Organization;
  65. query: string;
  66. queryCount: number;
  67. }) {
  68. return function ({
  69. action,
  70. canBeUndone,
  71. append = '',
  72. statusDetails,
  73. }: {
  74. action: ConfirmAction | string;
  75. canBeUndone: boolean;
  76. append?: string;
  77. statusDetails?: ResolutionStatusDetails;
  78. }) {
  79. const question = allInQuerySelected
  80. ? getBulkConfirmMessage(`${action}${append}`, queryCount)
  81. : tn(
  82. `Are you sure you want to ${action} this %s issue${append}?`,
  83. `Are you sure you want to ${action} these %s issues${append}?`,
  84. numIssues
  85. );
  86. let message: React.ReactNode;
  87. switch (action) {
  88. case ConfirmAction.DELETE:
  89. message = (
  90. <React.Fragment>
  91. <p>
  92. {tct(
  93. 'Bulk deletion is only recommended for junk data. To clear your stream, consider resolving or ignoring. [link:When should I delete events?]',
  94. {
  95. link: (
  96. <ExternalLink href="https://help.sentry.io/account/billing/when-should-i-delete-events/" />
  97. ),
  98. }
  99. )}
  100. </p>
  101. <PerformanceIssueAlert {...{organization, allInQuerySelected}}>
  102. {t('Deleting performance issues is not yet supported and will be skipped.')}
  103. </PerformanceIssueAlert>
  104. </React.Fragment>
  105. );
  106. break;
  107. case ConfirmAction.MERGE:
  108. message = (
  109. <React.Fragment>
  110. <p>{t('Note that unmerging is currently an experimental feature.')}</p>
  111. <PerformanceIssueAlert {...{organization, allInQuerySelected}}>
  112. {t('Merging performance issues is not yet supported and will be skipped.')}
  113. </PerformanceIssueAlert>
  114. </React.Fragment>
  115. );
  116. break;
  117. case ConfirmAction.IGNORE:
  118. if (statusDetails && !performanceIssuesSupportsIgnoreAction(statusDetails)) {
  119. message = (
  120. <PerformanceIssueAlert {...{organization, allInQuerySelected}}>
  121. {t(
  122. 'Ignoring performance issues by time window is not yet supported. Any encountered in this query will be skipped.'
  123. )}
  124. </PerformanceIssueAlert>
  125. );
  126. }
  127. break;
  128. default:
  129. message = !canBeUndone ? <p>{t('This action cannot be undone.')}</p> : null;
  130. }
  131. return (
  132. <div>
  133. <p style={{marginBottom: '20px'}}>
  134. <strong>{question}</strong>
  135. </p>
  136. <ExtraDescription
  137. all={allInQuerySelected}
  138. query={query}
  139. queryCount={queryCount}
  140. />
  141. {message}
  142. </div>
  143. );
  144. };
  145. }
  146. export function getLabel(numIssues: number, allInQuerySelected: boolean) {
  147. return function (action: string, append = '') {
  148. const capitalized = capitalize(action);
  149. const text = allInQuerySelected
  150. ? t(`Bulk ${action} issues`)
  151. : tn(
  152. `${capitalized} %s selected issue`,
  153. `${capitalized} %s selected issues`,
  154. numIssues
  155. );
  156. return text + append;
  157. };
  158. }
  159. export function performanceIssuesSupportsIgnoreAction(
  160. statusDetails: ResolutionStatusDetails
  161. ) {
  162. return !(statusDetails.ignoreWindow || statusDetails.ignoreUserWindow);
  163. }