import {useCallback} from 'react'; import styled from '@emotion/styled'; import {addErrorMessage, addLoadingMessage} from 'sentry/actionCreators/indicator'; import CheckboxField from 'sentry/components/forms/fields/checkboxField'; import SelectField from 'sentry/components/forms/fields/selectField'; import TextField from 'sentry/components/forms/fields/textField'; import Form from 'sentry/components/forms/form'; import type {OnSubmitCallback} from 'sentry/components/forms/types'; import HookOrDefault from 'sentry/components/hookOrDefault'; import NarrowLayout from 'sentry/components/narrowLayout'; import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle'; import {t, tct} from 'sentry/locale'; import ConfigStore from 'sentry/stores/configStore'; import HookStore from 'sentry/stores/hookStore'; import type {OrganizationSummary} from 'sentry/types/organization'; import {getRegionChoices, shouldDisplayRegions} from 'sentry/utils/regions'; import useApi from 'sentry/utils/useApi'; import {normalizeUrl} from 'sentry/utils/withDomainRequired'; export const DATA_STORAGE_DOCS_LINK = 'https://docs.sentry.io/product/accounts/choose-your-data-center'; function removeDataStorageLocationFromFormData( formData: Record ): Record { const shallowFormDataClone = {...formData}; delete shallowFormDataClone.dataStorageLocation; return shallowFormDataClone; } function OrganizationCreate() { const termsUrl = ConfigStore.get('termsUrl'); const privacyUrl = ConfigStore.get('privacyUrl'); const isSelfHosted = ConfigStore.get('isSelfHosted'); const relocationUrl = normalizeUrl(`/relocation/`); const regionChoices = getRegionChoices(); const client = useApi(); const DataConsentCheck = HookOrDefault({ hookName: 'component:data-consent-org-creation-checkbox', defaultComponent: null, }); const hasDataConsent = HookStore.get('component:data-consent-org-creation-checkbox').length !== 0; // This is a trimmed down version of the logic in ApiForm. It validates the // form data prior to submitting the request, and overrides the request host // with the selected region's URL if one is provided. const submitOrganizationCreate: OnSubmitCallback = useCallback( (data, onSubmitSuccess, onSubmitError, _event, formModel) => { if (!formModel.validateForm()) { return; } const regionUrl = data.dataStorageLocation; addLoadingMessage(t('Creating Organization\u2026')); formModel.setFormSaving(); client.request('/organizations/', { method: 'POST', data: removeDataStorageLocationFromFormData(data), host: regionUrl, success: onSubmitSuccess, error: onSubmitError, }); }, [client] ); return (

{t('Create a New Organization')}

{t( "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." )}

{ const hasCustomerDomain = ConfigStore.get('features').has('system:multi-region'); let nextUrl = normalizeUrl( `/organizations/${createdOrg.slug}/projects/new/`, {forceCustomerDomain: hasCustomerDomain} ); if (hasCustomerDomain) { nextUrl = `${createdOrg.links.organizationUrl}${nextUrl}`; } // redirect to project creation *(BYPASS REACT ROUTER AND FORCE PAGE REFRESH TO GRAB CSRF TOKEN)* // browserHistory.pushState(null, `/organizations/${data.slug}/projects/new/`); window.location.assign(nextUrl); }} onSubmitError={error => { addErrorMessage( error.responseJSON?.detail ?? t('Unable to create organization.') ); }} requireChanges > {shouldDisplayRegions() && ( } )} choices={regionChoices} inline={false} stacked required /> )} {termsUrl && privacyUrl && ( , privacyLink: , } )} inline={false} stacked required /> )} {!isSelfHosted && ConfigStore.get('features').has('relocation:enabled') && ( )}
); } export default OrganizationCreate; const TermsWrapper = styled('div')<{hasDataConsent?: boolean}>` margin-bottom: ${p => (p.hasDataConsent ? '0' : '16px')}; `;