body.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import {Fragment, useState} from 'react';
  2. import Alert from 'sentry/components/alert';
  3. import Button from 'sentry/components/button';
  4. import ErrorBoundary from 'sentry/components/errorBoundary';
  5. import Footer from 'sentry/components/footer';
  6. import {Body, Main} from 'sentry/components/layouts/thirds';
  7. import {t, tct} from 'sentry/locale';
  8. import AlertStore from 'sentry/stores/alertStore';
  9. import {Organization} from 'sentry/types';
  10. import useApi from 'sentry/utils/useApi';
  11. import withOrganization from 'sentry/utils/withOrganization';
  12. type Props = {
  13. organization: Organization;
  14. children?: React.ReactNode;
  15. };
  16. function DeletionInProgress({organization}: Props) {
  17. return (
  18. <Body>
  19. <Main>
  20. <Alert type="warning" showIcon>
  21. {tct(
  22. 'The [organization] organization is currently in the process of being deleted from Sentry.',
  23. {
  24. organization: <strong>{organization.slug}</strong>,
  25. }
  26. )}
  27. </Alert>
  28. </Main>
  29. </Body>
  30. );
  31. }
  32. function DeletionPending({organization}: Props) {
  33. const api = useApi();
  34. const [isRestoring, setIsRestoring] = useState(false);
  35. const onRestore = async () => {
  36. setIsRestoring(true);
  37. try {
  38. await api.requestPromise(`/organizations/${organization.slug}/`, {
  39. method: 'PUT',
  40. data: {cancelDeletion: true},
  41. });
  42. window.location.reload();
  43. } catch {
  44. setIsRestoring(false);
  45. AlertStore.addAlert({
  46. message:
  47. 'We were unable to restore this organization. Please try again or contact support.',
  48. type: 'error',
  49. });
  50. }
  51. };
  52. return (
  53. <Body>
  54. <Main>
  55. <h3>{t('Deletion Scheduled')}</h3>
  56. <p>
  57. {tct('The [organization] organization is currently scheduled for deletion.', {
  58. organization: <strong>{organization.slug}</strong>,
  59. })}
  60. </p>
  61. {organization.access.includes('org:admin') ? (
  62. <div>
  63. <p>
  64. {t(
  65. 'Would you like to cancel this process and restore the organization back to the original state?'
  66. )}
  67. </p>
  68. <p>
  69. <Button priority="primary" onClick={onRestore} disabled={isRestoring}>
  70. {t('Restore Organization')}
  71. </Button>
  72. </p>
  73. </div>
  74. ) : (
  75. <p>
  76. {t(
  77. 'If this is a mistake, contact an organization owner and ask them to restore this organization.'
  78. )}
  79. </p>
  80. )}
  81. <p>
  82. <small>
  83. {t(
  84. "Note: Restoration is available until the process begins. Once it does, there's no recovering the data that has been removed."
  85. )}
  86. </small>
  87. </p>
  88. </Main>
  89. </Body>
  90. );
  91. }
  92. function OrganizationDetailsBody({children, organization}: Props) {
  93. const status = organization?.status?.id;
  94. if (status === 'pending_deletion') {
  95. return <DeletionPending organization={organization} />;
  96. }
  97. if (status === 'deletion_in_progress') {
  98. return <DeletionInProgress organization={organization} />;
  99. }
  100. return (
  101. <Fragment>
  102. <ErrorBoundary>{children}</ErrorBoundary>
  103. <Footer />
  104. </Fragment>
  105. );
  106. }
  107. export default withOrganization(OrganizationDetailsBody);