index.tsx 5.0 KB

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