index.tsx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. import {Fragment} from 'react';
  2. import type {RouteComponentProps} from 'react-router';
  3. import {browserHistory} from 'react-router';
  4. import {addLoadingMessage} from 'sentry/actionCreators/indicator';
  5. import {
  6. changeOrganizationSlug,
  7. removeAndRedirectToRemainingOrganization,
  8. updateOrganization,
  9. } from 'sentry/actionCreators/organizations';
  10. import {Button} from 'sentry/components/button';
  11. import Confirm from 'sentry/components/confirm';
  12. import FieldGroup from 'sentry/components/forms/fieldGroup';
  13. import List from 'sentry/components/list';
  14. import ListItem from 'sentry/components/list/listItem';
  15. import Panel from 'sentry/components/panels/panel';
  16. import PanelHeader from 'sentry/components/panels/panelHeader';
  17. import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
  18. import {t, tct} from 'sentry/locale';
  19. import type {Organization} from 'sentry/types';
  20. import {trackAnalytics} from 'sentry/utils/analytics';
  21. import useApi from 'sentry/utils/useApi';
  22. import useOrganization from 'sentry/utils/useOrganization';
  23. import useProjects from 'sentry/utils/useProjects';
  24. import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';
  25. import TextBlock from 'sentry/views/settings/components/text/textBlock';
  26. import PermissionAlert from 'sentry/views/settings/organization/permissionAlert';
  27. import {OrganizationRegionAction} from 'sentry/views/settings/organizationGeneralSettings/organizationRegionAction';
  28. import OrganizationSettingsForm from './organizationSettingsForm';
  29. export default function OrganizationGeneralSettings({}: RouteComponentProps<{}, {}>) {
  30. const api = useApi();
  31. const organization = useOrganization();
  32. const {projects} = useProjects();
  33. const removeConfirmMessage = (
  34. <Fragment>
  35. <TextBlock>
  36. {tct(
  37. 'Removing the organization, [name] is permanent and cannot be undone! Are you sure you want to continue?',
  38. {
  39. name: organization && <strong>{organization.name}</strong>,
  40. }
  41. )}
  42. </TextBlock>
  43. {!!projects.length && (
  44. <Fragment>
  45. <TextBlock>
  46. {t('This will also remove the following associated projects:')}
  47. </TextBlock>
  48. <List symbol="bullet" data-test-id="removed-projects-list">
  49. {projects.map(project => (
  50. <ListItem key={project.slug}>{project.slug}</ListItem>
  51. ))}
  52. </List>
  53. </Fragment>
  54. )}
  55. </Fragment>
  56. );
  57. const handleSaveForm: React.ComponentProps<
  58. typeof OrganizationSettingsForm
  59. >['onSave'] = (prevData: Organization, updated: Organization) => {
  60. if (updated.slug && updated.slug !== prevData.slug) {
  61. changeOrganizationSlug(prevData, updated);
  62. if (updated.features.includes('customer-domains')) {
  63. const {organizationUrl} = updated.links;
  64. window.location.replace(`${organizationUrl}/settings/organization/`);
  65. } else {
  66. browserHistory.replace(`/settings/${updated.slug}/`);
  67. }
  68. } else {
  69. if (prevData.codecovAccess !== updated.codecovAccess) {
  70. trackAnalytics('organization_settings.codecov_access_updated', {
  71. organization: updated,
  72. has_access: updated.codecovAccess,
  73. });
  74. }
  75. // This will update OrganizationStore (as well as OrganizationsStore
  76. // which is slightly incorrect because it has summaries vs a detailed org)
  77. updateOrganization(updated);
  78. }
  79. };
  80. const handleConfirmRemoveOrg = () => {
  81. if (!organization) {
  82. return;
  83. }
  84. addLoadingMessage();
  85. removeAndRedirectToRemainingOrganization(api, {
  86. orgId: organization.slug,
  87. successMessage: `${organization.name} is queued for deletion.`,
  88. errorMessage: `Error removing the ${organization.name} organization`,
  89. });
  90. };
  91. const organizationRegionInfo = OrganizationRegionAction({
  92. organization,
  93. });
  94. return (
  95. <Fragment>
  96. <SentryDocumentTitle title={t('General Settings')} orgSlug={organization.slug} />
  97. <div>
  98. <SettingsPageHeader
  99. title={t('Organization Settings')}
  100. action={organizationRegionInfo}
  101. />
  102. <PermissionAlert />
  103. <OrganizationSettingsForm initialData={organization} onSave={handleSaveForm} />
  104. {organization.access.includes('org:admin') && !organization.isDefault && (
  105. <Panel>
  106. <PanelHeader>{t('Remove Organization')}</PanelHeader>
  107. <FieldGroup
  108. label={t('Remove Organization')}
  109. help={t(
  110. 'Removing this organization will delete all data including projects and their associated events.'
  111. )}
  112. >
  113. <div>
  114. <Confirm
  115. priority="danger"
  116. confirmText={t('Remove Organization')}
  117. message={removeConfirmMessage}
  118. onConfirm={handleConfirmRemoveOrg}
  119. >
  120. <Button priority="danger">{t('Remove Organization')}</Button>
  121. </Confirm>
  122. </div>
  123. </FieldGroup>
  124. </Panel>
  125. )}
  126. </div>
  127. </Fragment>
  128. );
  129. }