body.tsx 3.5 KB

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