index.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. import {useCallback} from 'react';
  2. import {addErrorMessage} from 'sentry/actionCreators/indicator';
  3. import CheckboxField from 'sentry/components/forms/fields/checkboxField';
  4. import SelectField from 'sentry/components/forms/fields/selectField';
  5. import TextField from 'sentry/components/forms/fields/textField';
  6. import Form from 'sentry/components/forms/form';
  7. import {OnSubmitCallback} from 'sentry/components/forms/types';
  8. import NarrowLayout from 'sentry/components/narrowLayout';
  9. import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
  10. import {t, tct} from 'sentry/locale';
  11. import ConfigStore from 'sentry/stores/configStore';
  12. import {OrganizationSummary} from 'sentry/types';
  13. import {getRegionChoices, shouldDisplayRegions} from 'sentry/utils/regions';
  14. import useApi from 'sentry/utils/useApi';
  15. import {normalizeUrl} from 'sentry/utils/withDomainRequired';
  16. function removeDataStorageLocationFromFormData(
  17. formData: Record<string, any>
  18. ): Record<string, any> {
  19. const shallowFormDataClone = {...formData};
  20. delete shallowFormDataClone.dataStorageLocation;
  21. return shallowFormDataClone;
  22. }
  23. function OrganizationCreate() {
  24. const termsUrl = ConfigStore.get('termsUrl');
  25. const privacyUrl = ConfigStore.get('privacyUrl');
  26. const regionChoices = getRegionChoices();
  27. const client = useApi();
  28. // This is a trimmed down version of the logic in ApiForm. It validates the
  29. // form data prior to submitting the request, and overrides the request host
  30. // with the selected region's URL if one is provided.
  31. const submitOrganizationCreate: OnSubmitCallback = useCallback(
  32. (data, onSubmitSuccess, onSubmitError, _event, formModel) => {
  33. if (!formModel.validateForm()) {
  34. return;
  35. }
  36. const regionUrl = data.dataStorageLocation;
  37. client.request('/organizations/', {
  38. method: 'POST',
  39. data: removeDataStorageLocationFromFormData(data),
  40. host: regionUrl,
  41. success: onSubmitSuccess,
  42. error: onSubmitError,
  43. });
  44. },
  45. [client]
  46. );
  47. return (
  48. <SentryDocumentTitle title={t('Create Organization')}>
  49. <NarrowLayout showLogout>
  50. <h3>{t('Create a New Organization')}</h3>
  51. <p>
  52. {t(
  53. "Organizations represent the top level in your hierarchy. You'll be able to bundle a collection of teams within an organization as well as give organization-wide permissions to users."
  54. )}
  55. </p>
  56. <Form
  57. initialData={{defaultTeam: true}}
  58. submitLabel={t('Create Organization')}
  59. apiEndpoint="/organizations/"
  60. apiMethod="POST"
  61. onSubmit={submitOrganizationCreate}
  62. onSubmitSuccess={(createdOrg: OrganizationSummary) => {
  63. const hasCustomerDomain = createdOrg?.features.includes('customer-domains');
  64. let nextUrl = normalizeUrl(
  65. `/organizations/${createdOrg.slug}/projects/new/`,
  66. {forceCustomerDomain: hasCustomerDomain}
  67. );
  68. if (hasCustomerDomain) {
  69. nextUrl = `${createdOrg.links.organizationUrl}${nextUrl}`;
  70. }
  71. // redirect to project creation *(BYPASS REACT ROUTER AND FORCE PAGE REFRESH TO GRAB CSRF TOKEN)*
  72. // browserHistory.pushState(null, `/organizations/${data.slug}/projects/new/`);
  73. window.location.assign(nextUrl);
  74. }}
  75. onSubmitError={error => {
  76. addErrorMessage(
  77. error.responseJSON?.detail ?? t('Unable to create organization.')
  78. );
  79. }}
  80. requireChanges
  81. >
  82. <TextField
  83. id="organization-name"
  84. name="name"
  85. label={t('Organization Name')}
  86. placeholder={t('e.g. My Company')}
  87. inline={false}
  88. flexibleControlStateSize
  89. stacked
  90. required
  91. />
  92. {shouldDisplayRegions() && (
  93. <SelectField
  94. name="dataStorageLocation"
  95. label="Data Storage Location"
  96. help="Where will this organization reside?"
  97. choices={regionChoices}
  98. inline={false}
  99. stacked
  100. required
  101. />
  102. )}
  103. {termsUrl && privacyUrl && (
  104. <CheckboxField
  105. name="agreeTerms"
  106. label={tct(
  107. 'I agree to the [termsLink:Terms of Service] and the [privacyLink:Privacy Policy]',
  108. {
  109. termsLink: <a href={termsUrl} />,
  110. privacyLink: <a href={privacyUrl} />,
  111. }
  112. )}
  113. inline={false}
  114. stacked
  115. required
  116. />
  117. )}
  118. </Form>
  119. </NarrowLayout>
  120. </SentryDocumentTitle>
  121. );
  122. }
  123. export default OrganizationCreate;