@@ -27,6 +27,7 @@ function CreateApplication() {
2727 const [ notification , setNotification ] = useState < { type : 'success' | 'error' ; message : string } | null > ( null )
2828 const [ , setErrors ] = useState < Record < string , string > > ( { } )
2929 const [ isCreating , setIsCreating ] = useState ( false )
30+ const [ partialSuccess , setPartialSuccess ] = useState < { applicationUuid : string ; instanceUuid : string } | null > ( null )
3031 const { user } = useAuth ( )
3132 const isAdmin = user ?. role === 'admin'
3233
@@ -55,36 +56,37 @@ function CreateApplication() {
5556 enabled : true ,
5657 } )
5758
59+ // Check if environment has any clusters
60+ const environmentClusters = useMemo ( ( ) => {
61+ if ( ! instanceData . environment_uuid || ! clusters ) return [ ]
62+ return clusters . filter (
63+ ( cluster ) => cluster . environment ?. uuid === instanceData . environment_uuid
64+ )
65+ } , [ instanceData . environment_uuid , clusters ] )
66+
67+ const hasNoClusters = instanceData . environment_uuid && environmentClusters . length === 0
68+
5869 // Check if any cluster in the selected environment has gateway_api available
5970 const hasGatewayApi = useMemo ( ( ) => {
6071 if ( ! instanceData . environment_uuid || ! clusters ) return false
61- const environmentClusters = clusters . filter (
62- ( cluster ) => cluster . environment ?. uuid === instanceData . environment_uuid
63- )
6472 return environmentClusters . some ( ( cluster ) => cluster . gateway ?. api ?. enabled === true )
65- } , [ instanceData . environment_uuid , clusters ] )
73+ } , [ instanceData . environment_uuid , clusters , environmentClusters ] )
6674
6775 // Get Gateway API resources available in clusters of the selected environment
6876 const gatewayResources = useMemo ( ( ) => {
69- if ( ! instanceData . environment_uuid || ! clusters ) return [ ]
70- const environmentClusters = clusters . filter (
71- ( cluster ) => cluster . environment ?. uuid === instanceData . environment_uuid
72- )
77+ if ( environmentClusters . length === 0 ) return [ ]
7378 const allResources = new Set < string > ( )
7479 environmentClusters . forEach ( ( cluster ) => {
7580 if ( cluster . gateway ?. api ?. enabled && cluster . gateway . api . resources ) {
7681 cluster . gateway . api . resources . forEach ( ( resource ) => allResources . add ( resource ) )
7782 }
7883 } )
7984 return Array . from ( allResources )
80- } , [ instanceData . environment_uuid , clusters ] )
85+ } , [ environmentClusters ] )
8186
8287 // Get Gateway reference from clusters of the selected environment
8388 const gatewayReference = useMemo ( ( ) => {
84- if ( ! instanceData . environment_uuid || ! clusters ) return { namespace : '' , name : '' }
85- const environmentClusters = clusters . filter (
86- ( cluster ) => cluster . environment ?. uuid === instanceData . environment_uuid
87- )
89+ if ( environmentClusters . length === 0 ) return { namespace : '' , name : '' }
8890 for ( const cluster of environmentClusters ) {
8991 if ( cluster . gateway ?. reference ) {
9092 const ref = cluster . gateway . reference . private || cluster . gateway . reference . public || { namespace : '' , name : '' }
@@ -96,7 +98,7 @@ function CreateApplication() {
9698 }
9799 }
98100 return { namespace : '' , name : '' }
99- } , [ instanceData . environment_uuid , clusters ] )
101+ } , [ environmentClusters ] )
100102
101103 // Components
102104 const [ components , setComponents ] = useState < ComponentFormData [ ] > ( [ ] )
@@ -228,6 +230,15 @@ function CreateApplication() {
228230 return
229231 }
230232
233+ // Check if environment has clusters
234+ if ( hasNoClusters ) {
235+ setNotification ( {
236+ type : 'error' ,
237+ message : 'The selected environment has no clusters. Please add a cluster in Settings → Clusters before creating components.' ,
238+ } )
239+ return
240+ }
241+
231242 setIsCreating ( true )
232243
233244 let application : { uuid : string } | null = null
@@ -297,34 +308,31 @@ function CreateApplication() {
297308 // eslint-disable-next-line @typescript-eslint/no-explicit-any
298309 } catch ( error : any ) {
299310 const errorMessage = error . response ?. data ?. detail || error . message || 'Error creating application'
311+ setIsCreating ( false )
300312
301- // If application and instance were created, redirect anyway (partial success)
313+ // If application and instance were created, show error but allow user to decide
302314 if ( application && instance ) {
315+ setPartialSuccess ( { applicationUuid : application . uuid , instanceUuid : instance . uuid } )
303316 setNotification ( {
304317 type : 'error' ,
305- message : `Application created but there was an error with components : ${ errorMessage } . Redirecting... ` ,
318+ message : `Application and instance created, but component failed : ${ errorMessage } ` ,
306319 } )
307- setTimeout ( ( ) => {
308- navigate ( `/applications/${ application ! . uuid } /instances/${ instance ! . uuid } /components` )
309- } , 2000 )
320+ // Show a button to navigate or let user fix the issue and retry
321+ // Don't auto-redirect - let user see the error and decide
310322 } else if ( application ) {
311323 // Application created but instance failed
312324 setNotification ( {
313325 type : 'error' ,
314- message : `Application created but instance failed: ${ errorMessage } . Redirecting... ` ,
326+ message : `Application created but instance failed: ${ errorMessage } ` ,
315327 } )
316- setTimeout ( ( ) => {
317- navigate ( `/applications/${ application ! . uuid } ` )
318- } , 2000 )
319328 } else {
320329 // Complete failure - allow retry
321- setIsCreating ( false )
322330 setNotification ( {
323331 type : 'error' ,
324332 message : errorMessage ,
325333 } )
326- setTimeout ( ( ) => setNotification ( null ) , 5000 )
327334 }
335+ // Don't auto-hide error notifications - let user dismiss them
328336 }
329337 }
330338
@@ -360,7 +368,23 @@ function CreateApplication() {
360368 )
361369
362370 const step2Content = (
363- < InstanceForm data = { instanceData } onChange = { setInstanceData } showInfoCard = { false } />
371+ < div className = "space-y-4" >
372+ < InstanceForm data = { instanceData } onChange = { setInstanceData } showInfoCard = { false } />
373+ { hasNoClusters && (
374+ < div className = "p-4 rounded-lg bg-amber-50 border border-amber-200 text-amber-800" >
375+ < div className = "flex items-start gap-2" >
376+ < span className = "text-amber-500 mt-0.5" > ⚠️</ span >
377+ < div >
378+ < p className = "font-medium" > No clusters in this environment</ p >
379+ < p className = "text-sm mt-1" >
380+ This environment has no clusters configured. You won't be able to deploy components until a cluster is added.
381+ Go to < span className = "font-medium" > Settings → Clusters</ span > to add one.
382+ </ p >
383+ </ div >
384+ </ div >
385+ </ div >
386+ ) }
387+ </ div >
364388 )
365389
366390 const step3Content = (
@@ -513,7 +537,42 @@ function CreateApplication() {
513537 notification . type === 'success' ? 'bg-green-50 text-green-800 border border-green-200' : 'bg-red-50 text-red-800 border border-red-200'
514538 } `}
515539 >
516- { notification . message }
540+ < div className = "flex flex-col gap-3" >
541+ < div className = "flex items-start justify-between" >
542+ < span > { notification . message } </ span >
543+ < button
544+ type = "button"
545+ onClick = { ( ) => {
546+ setNotification ( null )
547+ if ( ! partialSuccess ) setPartialSuccess ( null )
548+ } }
549+ className = "text-slate-400 hover:text-slate-600 ml-2"
550+ >
551+ ✕
552+ </ button >
553+ </ div >
554+ { partialSuccess && notification . type === 'error' && (
555+ < div className = "flex gap-2 mt-2" >
556+ < button
557+ type = "button"
558+ onClick = { ( ) => navigate ( `/applications/${ partialSuccess . applicationUuid } /instances/${ partialSuccess . instanceUuid } /components` ) }
559+ className = "px-3 py-1.5 text-sm bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition-colors"
560+ >
561+ Go to Instance (add components there)
562+ </ button >
563+ < button
564+ type = "button"
565+ onClick = { ( ) => {
566+ setNotification ( null )
567+ setPartialSuccess ( null )
568+ } }
569+ className = "px-3 py-1.5 text-sm bg-slate-100 text-slate-700 rounded-lg hover:bg-slate-200 transition-colors"
570+ >
571+ Stay and fix
572+ </ button >
573+ </ div >
574+ ) }
575+ </ div >
517576 </ div >
518577 ) }
519578
0 commit comments