index.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. import {addErrorMessage} from 'sentry/actionCreators/indicator';
  2. import SelectField from 'sentry/components/forms/fields/selectField';
  3. import Form from 'sentry/components/forms/form';
  4. import NarrowLayout from 'sentry/components/narrowLayout';
  5. import {t, tct} from 'sentry/locale';
  6. import ConfigStore from 'sentry/stores/configStore';
  7. import type {RouteComponentProps} from 'sentry/types/legacyReactRouter';
  8. import type {Organization} from 'sentry/types/organization';
  9. import type {Project} from 'sentry/types/project';
  10. import DeprecatedAsyncView from 'sentry/views/deprecatedAsyncView';
  11. import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';
  12. type Props = RouteComponentProps<{}, {}>;
  13. type TransferDetails = {
  14. organizations: Organization[];
  15. project: Project;
  16. };
  17. type State = {
  18. transferDetails: TransferDetails | null;
  19. } & DeprecatedAsyncView['state'];
  20. class AcceptProjectTransfer extends DeprecatedAsyncView<Props, State> {
  21. disableErrorReport = false;
  22. get regionHost(): string | undefined {
  23. // Because this route happens outside of OrganizationContext we
  24. // need to use initial data to decide which host to send the request to
  25. // as `/accept-transfer/` cannot be resolved to a region.
  26. const initialData = window.__initialData;
  27. let host: string | undefined = undefined;
  28. if (initialData && initialData.links?.regionUrl !== initialData.links?.sentryUrl) {
  29. host = initialData.links.regionUrl;
  30. }
  31. return host;
  32. }
  33. getEndpoints(): ReturnType<DeprecatedAsyncView['getEndpoints']> {
  34. const query = this.props.location.query;
  35. const host = this.regionHost;
  36. return [['transferDetails', '/accept-transfer/', {query, host}]];
  37. }
  38. getTitle() {
  39. return t('Accept Project Transfer');
  40. }
  41. handleSubmit = formData => {
  42. this.api.request('/accept-transfer/', {
  43. method: 'POST',
  44. host: this.regionHost,
  45. data: {
  46. data: this.props.location.query.data,
  47. organization: formData.organization,
  48. },
  49. success: () => {
  50. const orgSlug = formData.organization;
  51. const projectSlug = this.state?.transferDetails?.project.slug;
  52. const sentryUrl = ConfigStore.get('links').sentryUrl;
  53. if (!projectSlug) {
  54. window.location.href = `${sentryUrl}/organizations/${orgSlug}/projects/`;
  55. } else {
  56. window.location.href = `${sentryUrl}/organizations/${orgSlug}/settings/projects/${projectSlug}/teams/`;
  57. // done this way since we need to change subdomains
  58. }
  59. },
  60. error: error => {
  61. const errorMsg =
  62. error?.responseJSON && typeof error.responseJSON.detail === 'string'
  63. ? error.responseJSON.detail
  64. : '';
  65. addErrorMessage(
  66. t('Unable to transfer project') + errorMsg ? `: ${errorMsg}` : ''
  67. );
  68. },
  69. });
  70. };
  71. renderError(error) {
  72. let disableLog = false;
  73. // Check if there is an error message with `transferDetails` endpoint
  74. // If so, show as toast and ignore, otherwise log to sentry
  75. if (error?.responseJSON && typeof error.responseJSON.detail === 'string') {
  76. addErrorMessage(error.responseJSON.detail);
  77. disableLog = true;
  78. }
  79. return super.renderError(error, disableLog);
  80. }
  81. renderBody() {
  82. const {transferDetails} = this.state;
  83. const options = transferDetails?.organizations.map(org => ({
  84. label: org.slug,
  85. value: org.slug,
  86. }));
  87. const organization = options?.[0]?.value;
  88. return (
  89. <NarrowLayout>
  90. <SettingsPageHeader title={t('Approve Transfer Project Request')} />
  91. <p>
  92. {tct(
  93. 'Projects must be transferred to a specific [organization]. You can grant specific teams access to the project later under the [projectSettings]. (Note that granting access to at least one team is necessary for the project to appear in all parts of the UI.)',
  94. {
  95. organization: <strong>{t('Organization')}</strong>,
  96. projectSettings: <strong>{t('Project Settings')}</strong>,
  97. }
  98. )}
  99. </p>
  100. {transferDetails && (
  101. <p>
  102. {tct(
  103. 'Please select which [organization] you want for the project [project].',
  104. {
  105. organization: <strong>{t('Organization')}</strong>,
  106. project: transferDetails.project.slug,
  107. }
  108. )}
  109. </p>
  110. )}
  111. <Form
  112. onSubmit={this.handleSubmit}
  113. submitLabel={t('Transfer Project')}
  114. submitPriority="danger"
  115. initialData={organization ? {organization} : undefined}
  116. >
  117. <SelectField
  118. options={options}
  119. label={t('Organization')}
  120. name="organization"
  121. style={{borderBottom: 'none'}}
  122. />
  123. </Form>
  124. </NarrowLayout>
  125. );
  126. }
  127. }
  128. export default AcceptProjectTransfer;