reprocessEventModal.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import {Component, Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import {addErrorMessage} from 'sentry/actionCreators/indicator';
  4. import {ModalRenderProps} from 'sentry/actionCreators/modal';
  5. import Form from 'sentry/components/forms/form';
  6. import NumberField from 'sentry/components/forms/numberField';
  7. import RadioField from 'sentry/components/forms/radioField';
  8. import ExternalLink from 'sentry/components/links/externalLink';
  9. import List from 'sentry/components/list';
  10. import ListItem from 'sentry/components/list/listItem';
  11. import {t, tct} from 'sentry/locale';
  12. import space from 'sentry/styles/space';
  13. import {Group, Organization} from 'sentry/types';
  14. const impacts = [
  15. tct(
  16. "[strong:Quota applies.] Every event you choose to reprocess counts against your plan's quota. Rate limits and spike protection do not apply.",
  17. {strong: <strong />}
  18. ),
  19. tct(
  20. '[strong:Attachment storage required.] If your events come from minidumps or unreal crash reports, you must have [link:attachment storage] enabled.',
  21. {
  22. strong: <strong />,
  23. link: (
  24. <ExternalLink href="https://docs.sentry.io/platforms/native/enriching-events/attachments/#crash-reports-and-privacy" />
  25. ),
  26. }
  27. ),
  28. t(
  29. 'Please wait one hour after upload before attempting to reprocess missing debug files.'
  30. ),
  31. ];
  32. const remainingEventsChoices: [string, string][] = [
  33. ['keep', t('Keep')],
  34. ['delete', t('Delete')],
  35. ];
  36. export type ReprocessEventModalOptions = {
  37. groupId: Group['id'];
  38. organization: Organization;
  39. };
  40. type Props = ModalRenderProps & ReprocessEventModalOptions;
  41. type State = {
  42. maxEvents?: number;
  43. };
  44. class ReprocessingEventModal extends Component<Props, State> {
  45. state: State = {maxEvents: undefined};
  46. handleSuccess = () => {
  47. const {closeModal} = this.props;
  48. closeModal();
  49. window.location.reload();
  50. };
  51. handleError() {
  52. addErrorMessage(t('Failed to reprocess. Please check your input.'));
  53. }
  54. handleMaxEventsChange = (maxEvents: string) => {
  55. this.setState({maxEvents: Number(maxEvents) || undefined});
  56. };
  57. render() {
  58. const {organization, Header, Body, closeModal, groupId} = this.props;
  59. const {maxEvents} = this.state;
  60. const orgSlug = organization.slug;
  61. const endpoint = `/organizations/${orgSlug}/issues/${groupId}/reprocessing/`;
  62. const title = t('Reprocess Events');
  63. return (
  64. <Fragment>
  65. <Header closeButton>{title}</Header>
  66. <Body>
  67. <Introduction>
  68. {t(
  69. 'Reprocessing applies new debug files and grouping enhancements to this Issue. Please consider these impacts:'
  70. )}
  71. </Introduction>
  72. <StyledList symbol="bullet">
  73. {impacts.map((impact, index) => (
  74. <ListItem key={index}>{impact}</ListItem>
  75. ))}
  76. </StyledList>
  77. <Introduction>
  78. {tct('For more information, please refer to [link:the documentation.]', {
  79. link: (
  80. <ExternalLink href="https://docs.sentry.io/product/error-monitoring/reprocessing/" />
  81. ),
  82. })}
  83. </Introduction>
  84. <Form
  85. submitLabel={title}
  86. apiEndpoint={endpoint}
  87. apiMethod="POST"
  88. initialData={{maxEvents: undefined, remainingEvents: 'keep'}}
  89. onSubmitSuccess={this.handleSuccess}
  90. onSubmitError={this.handleError}
  91. onCancel={closeModal}
  92. footerClass="modal-footer"
  93. >
  94. <NumberField
  95. name="maxEvents"
  96. label={t('Number of events to be reprocessed')}
  97. help={t('If you set a limit, we will reprocess your most recent events.')}
  98. placeholder={t('Reprocess all events')}
  99. onChange={this.handleMaxEventsChange}
  100. min={1}
  101. />
  102. <RadioField
  103. orientInline
  104. label={t('Remaining events')}
  105. help={t('What to do with the events that are not reprocessed.')}
  106. name="remainingEvents"
  107. choices={remainingEventsChoices}
  108. disabled={maxEvents === undefined}
  109. />
  110. </Form>
  111. </Body>
  112. </Fragment>
  113. );
  114. }
  115. }
  116. export default ReprocessingEventModal;
  117. const Introduction = styled('p')`
  118. font-size: ${p => p.theme.fontSizeLarge};
  119. `;
  120. const StyledList = styled(List)`
  121. gap: ${space(1)};
  122. margin-bottom: ${space(4)};
  123. font-size: ${p => p.theme.fontSizeMedium};
  124. `;