@@ -7,15 +7,19 @@ import {Container, Flex, Stack} from '@sentry/scraps/layout';
77import { Text } from '@sentry/scraps/text' ;
88
99import { addErrorMessage } from 'sentry/actionCreators/indicator' ;
10+ import { update } from 'sentry/actionCreators/projects' ;
1011import { useOnboardingContext } from 'sentry/components/onboarding/onboardingContext' ;
1112import { useCreateProjectAndRules } from 'sentry/components/onboarding/useCreateProjectAndRules' ;
1213import { TeamSelector } from 'sentry/components/teamSelector' ;
1314import { IconGroup , IconProject , IconSiren } from 'sentry/icons' ;
1415import { t } from 'sentry/locale' ;
16+ import type { OnboardingSelectedSDK } from 'sentry/types/onboarding' ;
1517import type { Team } from 'sentry/types/organization' ;
1618import { trackAnalytics } from 'sentry/utils/analytics' ;
1719import { slugify } from 'sentry/utils/slugify' ;
20+ import { useApi } from 'sentry/utils/useApi' ;
1821import { useOrganization } from 'sentry/utils/useOrganization' ;
22+ import { useProjects } from 'sentry/utils/useProjects' ;
1923import { useTeams } from 'sentry/utils/useTeams' ;
2024import {
2125 DEFAULT_ISSUE_ALERT_OPTIONS_VALUES ,
@@ -32,15 +36,18 @@ import type {StepProps} from './types';
3236const PROJECT_DETAILS_WIDTH = '285px' ;
3337
3438export function ScmProjectDetails ( { onComplete} : StepProps ) {
39+ const api = useApi ( ) ;
3540 const organization = useOrganization ( ) ;
3641 const {
3742 selectedPlatform,
3843 selectedFeatures,
44+ createdProjectSlug,
3945 setCreatedProjectSlug,
4046 projectDetailsForm,
4147 setProjectDetailsForm,
4248 } = useOnboardingContext ( ) ;
43- const { teams} = useTeams ( ) ;
49+ const { teams, fetching : isLoadingTeams } = useTeams ( ) ;
50+ const { projects, initiallyLoaded : projectsLoaded } = useProjects ( ) ;
4451 const createProjectAndRules = useCreateProjectAndRules ( ) ;
4552 useEffect ( ( ) => {
4653 trackAnalytics ( 'onboarding.scm_project_details_step_viewed' , { organization} ) ;
@@ -58,6 +65,12 @@ export function ScmProjectDetails({onComplete}: StepProps) {
5865 projectDetailsForm ?. teamSlug ?? null
5966 ) ;
6067
68+ const [ isSaving , setIsSaving ] = useState ( false ) ;
69+
70+ // Safety-net: verify the previously created project still exists in the store.
71+ const projectExists =
72+ ! ! createdProjectSlug && projects . some ( p => p . slug === createdProjectSlug ) ;
73+
6174 const projectNameResolved = projectName ?? defaultName ;
6275 const teamSlugResolved = teamSlug ?? firstAdminTeam ?. slug ?? '' ;
6376
@@ -104,34 +117,35 @@ export function ScmProjectDetails({onComplete}: StepProps) {
104117 projectNameResolved . length > 0 &&
105118 teamSlugResolved . length > 0 &&
106119 ! ! selectedPlatform &&
107- ! createProjectAndRules . isPending ;
120+ ! isSaving &&
121+ ! isLoadingTeams &&
122+ projectsLoaded ;
108123
109- async function handleCreateProject ( ) {
110- if ( ! selectedPlatform || ! canSubmit ) {
124+ function persistFormState ( ) {
125+ if ( ! selectedPlatform ) {
111126 return ;
112127 }
128+ setProjectDetailsForm ( {
129+ projectName : projectNameResolved ,
130+ teamSlug : teamSlugResolved ,
131+ alertRuleConfig,
132+ platform : selectedPlatform . key ,
133+ } ) ;
134+ }
113135
114- trackAnalytics ( 'onboarding.scm_project_details_create_clicked' , { organization } ) ;
115-
136+ async function createNewProject ( platform : OnboardingSelectedSDK ) {
137+ setIsSaving ( true ) ;
116138 try {
117139 const { project} = await createProjectAndRules . mutateAsync ( {
118140 projectName : projectNameResolved ,
119- platform : selectedPlatform ,
141+ platform,
120142 team : teamSlugResolved ,
121143 alertRuleConfig : getRequestDataFragment ( alertRuleConfig ) ,
122144 createNotificationAction : ( ) => undefined ,
123145 } ) ;
124146
125- // Store the project slug separately so onboarding.tsx can find
126- // the project via useRecentCreatedProject without corrupting
127- // selectedPlatform.key (which the platform features step needs).
128147 setCreatedProjectSlug ( project . slug ) ;
129- setProjectDetailsForm ( {
130- projectName : projectNameResolved ,
131- teamSlug : teamSlugResolved ,
132- alertRuleConfig,
133- platform : selectedPlatform . key ,
134- } ) ;
148+ persistFormState ( ) ;
135149
136150 trackAnalytics ( 'onboarding.scm_project_details_create_succeeded' , {
137151 organization,
@@ -143,6 +157,54 @@ export function ScmProjectDetails({onComplete}: StepProps) {
143157 trackAnalytics ( 'onboarding.scm_project_details_create_failed' , { organization} ) ;
144158 addErrorMessage ( t ( 'Failed to create project' ) ) ;
145159 Sentry . captureException ( error ) ;
160+ } finally {
161+ setIsSaving ( false ) ;
162+ }
163+ }
164+
165+ async function updateExistingProject ( slug : string ) {
166+ setIsSaving ( true ) ;
167+ try {
168+ const updated = await update ( api , {
169+ orgId : organization . slug ,
170+ projectId : slug ,
171+ data : { name : projectNameResolved } ,
172+ } ) ;
173+
174+ setCreatedProjectSlug ( updated . slug ) ;
175+ persistFormState ( ) ;
176+ onComplete ( undefined , selectedFeatures ? { product : selectedFeatures } : undefined ) ;
177+ } catch ( error ) {
178+ addErrorMessage ( t ( 'Failed to update project' ) ) ;
179+ Sentry . captureException ( error ) ;
180+ } finally {
181+ setIsSaving ( false ) ;
182+ }
183+ }
184+
185+ function handleCreateProject ( ) {
186+ if ( ! selectedPlatform || ! canSubmit ) {
187+ return ;
188+ }
189+
190+ trackAnalytics ( 'onboarding.scm_project_details_create_clicked' , { organization} ) ;
191+
192+ if ( projectExists && createdProjectSlug ) {
193+ const platformChanged = projectDetailsForm ?. platform !== selectedPlatform . key ;
194+
195+ if ( platformChanged ) {
196+ // Platform changed — abandon the old project and create a new one,
197+ // matching legacy onboarding behavior.
198+ createNewProject ( selectedPlatform ) ;
199+ } else if ( projectName === null && teamSlug === null ) {
200+ // Nothing changed — reuse the existing project as-is.
201+ onComplete ( undefined , selectedFeatures ? { product : selectedFeatures } : undefined ) ;
202+ } else {
203+ // Same platform but name or team changed — update in place.
204+ updateExistingProject ( createdProjectSlug ) ;
205+ }
206+ } else {
207+ createNewProject ( selectedPlatform ) ;
146208 }
147209 }
148210
@@ -218,7 +280,7 @@ export function ScmProjectDetails({onComplete}: StepProps) {
218280 priority = "primary"
219281 onClick = { handleCreateProject }
220282 disabled = { ! canSubmit }
221- busy = { createProjectAndRules . isPending }
283+ busy = { isSaving }
222284 icon = { < IconProject /> }
223285 >
224286 { t ( 'Create project' ) }
0 commit comments