diff --git a/static/app/views/settings/components/settingsPageHeader.tsx b/static/app/views/settings/components/settingsPageHeader.tsx index d5a5e6b221d284..bd776dab30a33e 100644 --- a/static/app/views/settings/components/settingsPageHeader.tsx +++ b/static/app/views/settings/components/settingsPageHeader.tsx @@ -1,8 +1,11 @@ +import {Fragment} from 'react'; import styled from '@emotion/styled'; import * as Layout from 'sentry/components/layouts/thirds'; +import {useRoutes} from 'sentry/utils/useRoutes'; import {TopBar} from 'sentry/views/navigation/topBar'; import {useHasPageFrameFeature} from 'sentry/views/navigation/useHasPageFrameFeature'; +import {BreadcrumbTitle} from 'sentry/views/settings/components/settingsBreadcrumb/breadcrumbTitle'; type Props = { /** @@ -42,12 +45,27 @@ function UnstyledSettingsPageHeader({ noTitleStyles = false, ...props }: Props) { + const routes = useRoutes(); const hasPageFrame = useHasPageFrameFeature(); // If Header is narrow, use align-items to center . // Otherwise, use a fixed margin to prevent an odd alignment. // This is needed as Actions could be a button or a dropdown. const isNarrow = !subtitle; + // In page frame mode the breadcrumb in the TopBar serves as the page title. + // Sync the last breadcrumb label with the actual page title and skip + // rendering the title heading so it doesn't appear twice. + if (hasPageFrame) { + return ( + + {typeof title === 'string' && } + {action && {action}} + {body && {body}} + {tabs && {tabs}} + + ); + } + return (
@@ -60,13 +78,7 @@ function UnstyledSettingsPageHeader({ )} - {action ? ( - hasPageFrame ? ( - {action} - ) : ( - {action} - ) - ) : null} + {action && {action}} {body && {body}} diff --git a/static/app/views/settings/organizationGeneralSettings/index.tsx b/static/app/views/settings/organizationGeneralSettings/index.tsx index ed5d80fbf4d35f..ccc3a30a9370d8 100644 --- a/static/app/views/settings/organizationGeneralSettings/index.tsx +++ b/static/app/views/settings/organizationGeneralSettings/index.tsx @@ -23,6 +23,7 @@ import {useApi} from 'sentry/utils/useApi'; import {useNavigate} from 'sentry/utils/useNavigate'; import {useOrganization} from 'sentry/utils/useOrganization'; import {useProjects} from 'sentry/utils/useProjects'; +import {useHasPageFrameFeature} from 'sentry/views/navigation/useHasPageFrameFeature'; import {SettingsPageHeader} from 'sentry/views/settings/components/settingsPageHeader'; import {TextBlock} from 'sentry/views/settings/components/text/textBlock'; import {OrganizationPermissionAlert} from 'sentry/views/settings/organization/organizationPermissionAlert'; @@ -36,6 +37,7 @@ export default function OrganizationGeneralSettings() { const organization = useOrganization(); const {projects} = useProjects(); const navigate = useNavigate(); + const hasPageFrameFeature = useHasPageFrameFeature(); const removeConfirmMessage = ( @@ -113,7 +115,7 @@ export default function OrganizationGeneralSettings() {
diff --git a/static/app/views/settings/organizationGeneralSettings/organizationSettingsForm.tsx b/static/app/views/settings/organizationGeneralSettings/organizationSettingsForm.tsx index 49bf657802f9ab..559aab699a7ce9 100644 --- a/static/app/views/settings/organizationGeneralSettings/organizationSettingsForm.tsx +++ b/static/app/views/settings/organizationGeneralSettings/organizationSettingsForm.tsx @@ -14,8 +14,9 @@ import { setFieldErrors, useScrapsForm, } from '@sentry/scraps/form'; -import {Container, Flex} from '@sentry/scraps/layout'; +import {Container, Flex, Stack} from '@sentry/scraps/layout'; import {ExternalLink} from '@sentry/scraps/link'; +import {Text} from '@sentry/scraps/text'; import {addErrorMessage} from 'sentry/actionCreators/indicator'; import {updateOrganization} from 'sentry/actionCreators/organizations'; @@ -30,10 +31,13 @@ import {ConfigStore} from 'sentry/stores/configStore'; import type {MembershipSettingsProps} from 'sentry/types/hooks'; import type {Organization} from 'sentry/types/organization'; import {fetchMutation, useMutation} from 'sentry/utils/queryClient'; +import {getRegionDataFromOrganization, getRegions} from 'sentry/utils/regions'; import {RequestError} from 'sentry/utils/requestError/requestError'; import {slugify} from 'sentry/utils/slugify'; import {useMembers} from 'sentry/utils/useMembers'; import {useOrganization} from 'sentry/utils/useOrganization'; +import {useHasPageFrameFeature} from 'sentry/views/navigation/useHasPageFrameFeature'; +import {DATA_STORAGE_DOCS_LINK} from 'sentry/views/organizationCreate'; const HookCodecovSettingsLink = HookOrDefault({ hookName: 'component:codecov-integration-settings-link', @@ -415,10 +419,13 @@ function OrganizationMembershipSettingsBase({ export function OrganizationSettingsForm({initialData, onSave}: Props) { const organization = useOrganization(); const endpoint = `/organizations/${organization.slug}/`; + const hasPageFrameFeature = useHasPageFrameFeature(); const access = useMemo(() => new Set(organization.access), [organization]); const hasWriteAccess = access.has('org:write'); const hasGenAiFeatureFlag = organization.features.includes('gen-ai-features'); + const regionData = + getRegions().length > 1 ? getRegionDataFromOrganization(organization) : null; const aiEnabled = hasGenAiFeatureFlag ? (initialData.hideAiFeatures ?? false) : false; @@ -545,6 +552,23 @@ export function OrganizationSettingsForm({initialData, onSave}: Props) { )} + {/* Data Storage Region — read-only, only shown when multiple regions exist */} + {hasPageFrameFeature && regionData && ( + + + {t('Data Storage Region')} + + {tct("Your organization's data storage location. [link:Learn More]", { + link: , + })} + + + + {`${regionData.flag} ${regionData.displayName}`} + + + )} + {/* Early Adopter — hidden for self-hosted errors-only */} {!ConfigStore.get('isSelfHostedErrorsOnly') && (