index.tsx 5.1 KB

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