body.tsx 3.5 KB

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