index.tsx 4.9 KB

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