body.tsx 3.4 KB

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