1- import React , { useEffect , useRef } from 'react' ;
1+ import React , { useEffect , useMemo , useRef , useState } from 'react' ;
22import { ComponentsSelection } from './ComponentsSelection.tsx' ;
33
44import IllustratedError from '../Shared/IllustratedError.tsx' ;
@@ -9,10 +9,12 @@ import { useApiResource } from '../../lib/api/useApiResource.ts';
99import Loading from '../Shared/Loading.tsx' ;
1010import { ComponentsListItem , removeComponents } from '../../lib/api/types/crate/createManagedControlPlane.ts' ;
1111import { useTranslation } from 'react-i18next' ;
12+ import { ManagedControlPlaneTemplate } from '../../lib/api/types/templates/mcpTemplate.ts' ;
1213
1314export interface ComponentsSelectionProps {
1415 componentsList : ComponentsListItem [ ] ;
1516 setComponentsList : ( components : ComponentsListItem [ ] ) => void ;
17+ managedControlPlaneTemplate ?: ManagedControlPlaneTemplate ;
1618 initialSelection ?: Record < string , { isSelected : boolean ; version : string } > ;
1719}
1820
@@ -31,14 +33,27 @@ export const getSelectedComponents = (components: ComponentsListItem[]) => {
3133 } ) ;
3234} ;
3335
36+ type TemplateDefaultComponent = {
37+ name : string ;
38+ version : string ;
39+ removable ?: boolean ;
40+ versionChangeable ?: boolean ;
41+ } ;
42+
3443export const ComponentsSelectionContainer : React . FC < ComponentsSelectionProps > = ( {
3544 setComponentsList,
3645 componentsList,
46+ managedControlPlaneTemplate,
3747 initialSelection,
3848} ) => {
3949 const { data : availableManagedComponentsListData , error, isLoading } = useApiResource ( ListManagedComponents ( ) ) ;
4050 const { t } = useTranslation ( ) ;
4151 const initialized = useRef ( false ) ;
52+ const [ templateDefaultsError , setTemplateDefaultsError ] = useState < string | null > ( null ) ;
53+ const defaultComponents = useMemo < TemplateDefaultComponent [ ] > (
54+ ( ) => managedControlPlaneTemplate ?. spec ?. spec ?. components ?. defaultComponents ?? [ ] ,
55+ [ managedControlPlaneTemplate ] ,
56+ ) ;
4257
4358 useEffect ( ( ) => {
4459 if (
@@ -49,37 +64,83 @@ export const ComponentsSelectionContainer: React.FC<ComponentsSelectionProps> =
4964 return ;
5065 }
5166
52- let newComponentsList = availableManagedComponentsListData . items
67+ const newComponentsList = availableManagedComponentsListData . items
5368 . map ( ( item ) => {
5469 const versions = sortVersions ( item . status . versions ) ;
70+ const template = defaultComponents . find ( ( dc ) => dc . name === item . metadata . name ) ;
71+ const templateVersion = template ?. version ;
72+ let selectedVersion = template
73+ ? templateVersion && versions . includes ( templateVersion )
74+ ? templateVersion
75+ : ''
76+ : ( versions [ 0 ] ?? '' ) ;
77+ let isSelected = ! ! template ;
78+
79+ const initSel = initialSelection ?. [ item . metadata . name ] ;
80+ if ( initSel ) {
81+ // Override selection and version from initial selection if provided
82+ isSelected = Boolean ( initSel . isSelected ) ;
83+ selectedVersion = initSel . version && versions . includes ( initSel . version ) ? initSel . version : '' ;
84+ }
5585 return {
5686 name : item . metadata . name ,
5787 versions,
58- selectedVersion : versions [ 0 ] ?? '' ,
59- isSelected : false ,
88+ selectedVersion,
89+ isSelected,
6090 documentationUrl : '' ,
6191 } ;
6292 } )
6393 . filter ( ( component ) => ! removeComponents . find ( ( item ) => item === component . name ) ) ;
6494
65- // Apply initial selection when provided (edit mode)
66- if ( initialSelection ) {
67- newComponentsList = newComponentsList . map ( ( component ) => {
68- const init = initialSelection [ component . name ] ;
69- if ( ! init ) return component ;
70- // Ensure selectedVersion exists in versions list; if not, keep as-is
71- const selectedVersion = component . versions . includes ( init . version ) ? init . version : component . selectedVersion ;
72- return {
73- ...component ,
74- isSelected : init . isSelected ,
75- selectedVersion,
76- } ;
77- } ) ;
78- }
79-
8095 setComponentsList ( newComponentsList ) ;
8196 initialized . current = true ;
82- } , [ availableManagedComponentsListData , setComponentsList , initialSelection ] ) ;
97+ } , [ availableManagedComponentsListData , setComponentsList , defaultComponents , initialSelection ] ) ;
98+
99+ useEffect ( ( ) => {
100+ const items = availableManagedComponentsListData ?. items ?? [ ] ;
101+ if ( items . length === 0 || ! defaultComponents . length ) {
102+ setTemplateDefaultsError ( null ) ;
103+ return ;
104+ }
105+
106+ const errs : string [ ] = [ ] ;
107+ defaultComponents . forEach ( ( dc : TemplateDefaultComponent ) => {
108+ if ( ! dc ?. name ) return ;
109+ const item = items . find ( ( it ) => it . metadata . name === dc . name ) ;
110+ if ( ! item ) {
111+ errs . push ( `Component "${ dc . name } " from template is not available.` ) ;
112+ return ;
113+ }
114+ const versions : string [ ] = Array . isArray ( item . status ?. versions ) ? item . status . versions : [ ] ;
115+ if ( dc . version && ! versions . includes ( dc . version ) ) {
116+ errs . push ( `Component "${ dc . name } " version "${ dc . version } " from template is not available.` ) ;
117+ }
118+ } ) ;
119+
120+ setTemplateDefaultsError ( errs . length ? errs . join ( '\n' ) : null ) ;
121+ } , [ availableManagedComponentsListData , defaultComponents ] ) ;
122+
123+ useEffect ( ( ) => {
124+ if ( ! initialized . current ) return ;
125+ if ( ! defaultComponents ?. length ) return ;
126+ if ( ! componentsList ?. length ) return ;
127+ // If initialSelection is provided, do not auto-apply template defaults
128+ if ( initialSelection && Object . keys ( initialSelection ) . length > 0 ) return ;
129+
130+ const anySelected = componentsList . some ( ( c ) => c . isSelected ) ;
131+ if ( anySelected ) return ;
132+
133+ const updated = componentsList . map ( ( c ) => {
134+ const template = defaultComponents . find ( ( dc ) => dc . name === c . name ) ;
135+ if ( ! template ) return c ;
136+ const templateVersion = template . version ;
137+ const selectedVersion =
138+ templateVersion && Array . isArray ( c . versions ) && c . versions . includes ( templateVersion ) ? templateVersion : '' ;
139+ return { ...c , isSelected : true , selectedVersion } ;
140+ } ) ;
141+
142+ setComponentsList ( updated ) ;
143+ } , [ defaultComponents , componentsList , setComponentsList ] ) ;
83144
84145 if ( isLoading ) {
85146 return < Loading /> ;
@@ -94,5 +155,11 @@ export const ComponentsSelectionContainer: React.FC<ComponentsSelectionProps> =
94155 return < IllustratedError title = { t ( 'componentsSelection.cannotLoad' ) } compact = { true } /> ;
95156 }
96157
97- return < ComponentsSelection componentsList = { componentsList } setComponentsList = { setComponentsList } /> ;
158+ return (
159+ < ComponentsSelection
160+ componentsList = { componentsList }
161+ setComponentsList = { setComponentsList }
162+ templateDefaultsError = { templateDefaultsError || undefined }
163+ />
164+ ) ;
98165} ;
0 commit comments