From efbb271583788dd26843ce951c2072df8116a4b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Goral?= Date: Tue, 23 Sep 2025 11:44:21 +0200 Subject: [PATCH 01/37] refactor --- .../ComponentsSelectionContainer.tsx | 129 +------------- .../ComponentsSelectionProvider.tsx | 163 ++++++++++++++++++ ...eateManagedControlPlaneWizardContainer.tsx | 10 +- 3 files changed, 175 insertions(+), 127 deletions(-) create mode 100644 src/components/ComponentsSelection/ComponentsSelectionProvider.tsx diff --git a/src/components/ComponentsSelection/ComponentsSelectionContainer.tsx b/src/components/ComponentsSelection/ComponentsSelectionContainer.tsx index 16aeb250..8e55784e 100644 --- a/src/components/ComponentsSelection/ComponentsSelectionContainer.tsx +++ b/src/components/ComponentsSelection/ComponentsSelectionContainer.tsx @@ -1,24 +1,14 @@ -import React, { useEffect, useMemo, useState } from 'react'; +import React from 'react'; import { ComponentsSelection } from './ComponentsSelection.tsx'; - import IllustratedError from '../Shared/IllustratedError.tsx'; -import { sortVersions } from '../../utils/componentsVersions.ts'; - -import { ListManagedComponents } from '../../lib/api/types/crate/listManagedComponents.ts'; -import { useApiResource } from '../../lib/api/useApiResource.ts'; import Loading from '../Shared/Loading.tsx'; -import { ComponentsListItem, removeComponents } from '../../lib/api/types/crate/createManagedControlPlane.ts'; +import { ComponentsListItem } from '../../lib/api/types/crate/createManagedControlPlane.ts'; import { useTranslation } from 'react-i18next'; -import { ManagedControlPlaneTemplate } from '../../lib/api/types/templates/mcpTemplate.ts'; +import { useComponentsSelection } from './ComponentsSelectionProvider.tsx'; export interface ComponentsSelectionProps { componentsList: ComponentsListItem[]; setComponentsList: (components: ComponentsListItem[]) => void; - setInitialComponentsList: (components: ComponentsListItem[]) => void; - managedControlPlaneTemplate?: ManagedControlPlaneTemplate; - initialSelection?: Record; - isOnMcpPage?: boolean; - initializedComponents: React.RefObject; } /** @@ -34,123 +24,12 @@ export const getSelectedComponents = (components: ComponentsListItem[]) => { }); }; -type TemplateDefaultComponent = { - name: string; - version: string; - removable?: boolean; - versionChangeable?: boolean; -}; - export const ComponentsSelectionContainer: React.FC = ({ setComponentsList, componentsList, - managedControlPlaneTemplate, - initialSelection, - isOnMcpPage, - setInitialComponentsList, - initializedComponents, }) => { - const { - data: availableManagedComponentsListData, - error, - isLoading, - } = useApiResource(ListManagedComponents(), undefined, !!isOnMcpPage); const { t } = useTranslation(); - - const [templateDefaultsError, setTemplateDefaultsError] = useState(null); - const defaultComponents = useMemo( - () => managedControlPlaneTemplate?.spec?.spec?.components?.defaultComponents ?? [], - [managedControlPlaneTemplate], - ); - - useEffect(() => { - if ( - initializedComponents.current || - !availableManagedComponentsListData?.items || - availableManagedComponentsListData.items.length === 0 - ) { - return; - } - - const newComponentsList = availableManagedComponentsListData.items - .map((item) => { - const versions = sortVersions(item.status?.versions ?? []); - const template = defaultComponents.find((dc) => dc.name === (item.metadata?.name ?? '')); - const templateVersion = template?.version; - let selectedVersion = template - ? templateVersion && versions.includes(templateVersion) - ? templateVersion - : '' - : (versions[0] ?? ''); - let isSelected = !!template; - - const initSel = initialSelection?.[item.metadata?.name ?? '']; - if (initSel) { - // Override selection and version from initial selection if provided - isSelected = Boolean(initSel.isSelected); - selectedVersion = initSel.version && versions.includes(initSel.version) ? initSel.version : ''; - } - return { - name: item.metadata?.name ?? '', - versions, - selectedVersion, - isSelected, - documentationUrl: '', - }; - }) - .filter((component) => !removeComponents.find((item) => item === component.name)); - setInitialComponentsList(newComponentsList); - setComponentsList(newComponentsList); - initializedComponents.current = true; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [setComponentsList, defaultComponents, initialSelection, availableManagedComponentsListData?.items]); - - useEffect(() => { - const items = availableManagedComponentsListData?.items ?? []; - if (items.length === 0 || !defaultComponents.length) { - setTemplateDefaultsError(null); - return; - } - - const errors: string[] = []; - defaultComponents.forEach((dc: TemplateDefaultComponent) => { - if (!dc?.name) return; - const item = items.find((it) => it.metadata?.name === dc.name); - if (!item) { - errors.push(`Component "${dc.name}" from template is not available.`); - return; - } - const versions: string[] = Array.isArray(item.status?.versions) ? (item.status?.versions as string[]) : []; - if (dc.version && !versions.includes(dc.version)) { - errors.push(`Component "${dc.name}" version "${dc.version}" from template is not available.`); - } - }); - - setTemplateDefaultsError(errors.length ? errors.join('\n') : null); - }, [availableManagedComponentsListData, defaultComponents]); - - useEffect(() => { - if (!initializedComponents.current) return; - if (!defaultComponents?.length) return; - if (!componentsList?.length) return; - // If initialSelection is provided, do not auto-apply template defaults - if (initialSelection && Object.keys(initialSelection).length > 0) return; - - const anySelected = componentsList.some((c) => c.isSelected); - if (anySelected) return; - - const updated = componentsList.map((c) => { - const template = defaultComponents.find((dc) => dc.name === c.name); - if (!template) return c; - const templateVersion = template.version; - const selectedVersion = - templateVersion && Array.isArray(c.versions) && c.versions.includes(templateVersion) ? templateVersion : ''; - return { ...c, isSelected: true, selectedVersion }; - }); - - setComponentsList(updated); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [defaultComponents, componentsList, setComponentsList, initialSelection]); + const { isLoading, error, templateDefaultsError } = useComponentsSelection(); if (isLoading) { return ; diff --git a/src/components/ComponentsSelection/ComponentsSelectionProvider.tsx b/src/components/ComponentsSelection/ComponentsSelectionProvider.tsx new file mode 100644 index 00000000..99675d74 --- /dev/null +++ b/src/components/ComponentsSelection/ComponentsSelectionProvider.tsx @@ -0,0 +1,163 @@ +import React, { createContext, useContext, useEffect, useMemo, useState } from 'react'; +import { useApiResource } from '../../lib/api/useApiResource.ts'; +import { ListManagedComponents } from '../../lib/api/types/crate/listManagedComponents.ts'; +import { sortVersions } from '../../utils/componentsVersions.ts'; +import { ComponentsListItem, removeComponents } from '../../lib/api/types/crate/createManagedControlPlane.ts'; +import { ManagedControlPlaneTemplate } from '../../lib/api/types/templates/mcpTemplate.ts'; + +export type ComponentsSelectionProviderProps = { + componentsList: ComponentsListItem[]; + setComponentsList: (components: ComponentsListItem[]) => void; + setInitialComponentsList: (components: ComponentsListItem[]) => void; + managedControlPlaneTemplate?: ManagedControlPlaneTemplate; + initialSelection?: Record; + isOnMcpPage?: boolean; + initializedComponents: React.RefObject; + children: React.ReactNode; +}; + +export type ComponentsSelectionContextValue = { + isLoading: boolean; + error: unknown; + templateDefaultsError: string | null; +}; + +const ComponentsSelectionContext = createContext(undefined); + +export const useComponentsSelection = (): ComponentsSelectionContextValue => { + const ctx = useContext(ComponentsSelectionContext); + if (!ctx) { + throw new Error('useComponentsSelection must be used within ComponentsSelectionProvider'); + } + return ctx; +}; + +export const ComponentsSelectionProvider: React.FC = ({ + componentsList, + setComponentsList, + setInitialComponentsList, + managedControlPlaneTemplate, + initialSelection, + isOnMcpPage, + initializedComponents, + children, +}) => { + type TemplateDefaultComponent = { + name: string; + version: string; + removable?: boolean; + versionChangeable?: boolean; + }; + + const { + data: availableManagedComponentsListData, + error, + isLoading, + } = useApiResource(ListManagedComponents(), undefined, !!isOnMcpPage); + + const [templateDefaultsError, setTemplateDefaultsError] = useState(null); + + const defaultComponents = useMemo( + () => managedControlPlaneTemplate?.spec?.spec?.components?.defaultComponents ?? [], + [managedControlPlaneTemplate], + ); + + // Initialize components list from API + template/defaults/initialSelection + useEffect(() => { + if ( + initializedComponents.current || + !availableManagedComponentsListData?.items || + availableManagedComponentsListData.items.length === 0 + ) { + return; + } + + const newComponentsList = availableManagedComponentsListData.items + .map((item) => { + const versions = sortVersions(item.status?.versions ?? []); + const template = defaultComponents.find((dc) => dc.name === (item.metadata?.name ?? '')); + const templateVersion = template?.version; + let selectedVersion = template + ? templateVersion && versions.includes(templateVersion) + ? templateVersion + : '' + : (versions[0] ?? ''); + let isSelected = !!template; + + const initSel = initialSelection?.[item.metadata?.name ?? '']; + if (initSel) { + isSelected = Boolean(initSel.isSelected); + selectedVersion = initSel.version && versions.includes(initSel.version) ? initSel.version : ''; + } + return { + name: item.metadata?.name ?? '', + versions, + selectedVersion, + isSelected, + documentationUrl: '', + } as ComponentsListItem; + }) + .filter((component) => !removeComponents.find((item) => item === component.name)); + + setInitialComponentsList(newComponentsList); + setComponentsList(newComponentsList); + initializedComponents.current = true; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [setComponentsList, defaultComponents, initialSelection, availableManagedComponentsListData?.items]); + + // Validate template default components are available + useEffect(() => { + const items = availableManagedComponentsListData?.items ?? []; + if (items.length === 0 || !defaultComponents.length) { + setTemplateDefaultsError(null); + return; + } + + const errors: string[] = []; + defaultComponents.forEach((dc: TemplateDefaultComponent) => { + if (!dc?.name) return; + const item = items.find((it) => it.metadata?.name === dc.name); + if (!item) { + errors.push(`Component "${dc.name}" from template is not available.`); + return; + } + const versions: string[] = Array.isArray(item.status?.versions) ? (item.status?.versions as string[]) : []; + if (dc.version && !versions.includes(dc.version)) { + errors.push(`Component "${dc.name}" version "${dc.version}" from template is not available.`); + } + }); + + setTemplateDefaultsError(errors.length ? errors.join('\n') : null); + }, [availableManagedComponentsListData, defaultComponents]); + + // Auto-apply template defaults if none selected and no initialSelection + useEffect(() => { + if (!initializedComponents.current) return; + if (!defaultComponents?.length) return; + if (!componentsList?.length) return; + if (initialSelection && Object.keys(initialSelection).length > 0) return; + + const anySelected = componentsList.some((c) => c.isSelected); + if (anySelected) return; + + const updated = componentsList.map((c) => { + const template = defaultComponents.find((dc) => dc.name === c.name); + if (!template) return c; + const templateVersion = template.version; + const selectedVersion = + templateVersion && Array.isArray(c.versions) && c.versions.includes(templateVersion) ? templateVersion : ''; + return { ...c, isSelected: true, selectedVersion } as ComponentsListItem; + }); + + setComponentsList(updated); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [defaultComponents, componentsList, setComponentsList, initialSelection]); + + const value: ComponentsSelectionContextValue = { + isLoading: Boolean(isLoading), + error, + templateDefaultsError, + }; + + return {children}; +}; diff --git a/src/components/Wizards/CreateManagedControlPlane/CreateManagedControlPlaneWizardContainer.tsx b/src/components/Wizards/CreateManagedControlPlane/CreateManagedControlPlaneWizardContainer.tsx index 45744a5e..b18dcab9 100644 --- a/src/components/Wizards/CreateManagedControlPlane/CreateManagedControlPlaneWizardContainer.tsx +++ b/src/components/Wizards/CreateManagedControlPlane/CreateManagedControlPlaneWizardContainer.tsx @@ -44,6 +44,7 @@ import { APIError } from '../../../lib/api/error.ts'; import { MetadataForm } from '../../Dialogs/MetadataForm.tsx'; import { EditMembers } from '../../Members/EditMembers.tsx'; import { ComponentsSelectionContainer } from '../../ComponentsSelection/ComponentsSelectionContainer.tsx'; +import { ComponentsSelectionProvider } from '../../ComponentsSelection/ComponentsSelectionProvider.tsx'; import { IllustratedBanner } from '../../Ui/IllustratedBanner/IllustratedBanner.tsx'; import { ManagedControlPlaneTemplate, noTemplateValue } from '../../../lib/api/types/templates/mcpTemplate.ts'; import { stripIdpPrefix } from '../../../utils/stripIdpPrefix.ts'; @@ -599,7 +600,7 @@ export const CreateManagedControlPlaneWizardContainer: FC {/* this condition is to remount the component from scratch to fix a bug with data loading */} {selectedStep === 'componentSelection' && ( - + > + + )} Date: Tue, 23 Sep 2025 12:41:18 +0200 Subject: [PATCH 02/37] Update CreateManagedControlPlaneWizardContainer.tsx --- ...eateManagedControlPlaneWizardContainer.tsx | 246 +++++++++--------- 1 file changed, 124 insertions(+), 122 deletions(-) diff --git a/src/components/Wizards/CreateManagedControlPlane/CreateManagedControlPlaneWizardContainer.tsx b/src/components/Wizards/CreateManagedControlPlane/CreateManagedControlPlaneWizardContainer.tsx index b18dcab9..416f5ee5 100644 --- a/src/components/Wizards/CreateManagedControlPlane/CreateManagedControlPlaneWizardContainer.tsx +++ b/src/components/Wizards/CreateManagedControlPlane/CreateManagedControlPlaneWizardContainer.tsx @@ -87,7 +87,7 @@ export const CreateManagedControlPlaneWizardContainer: FC(null); - const [selectedStep, setSelectedStep] = useState('metadata'); + const [selectedStep, setSelectedStep] = useState('summarize'); const [metadataFormKey, setMetadataFormKey] = useState(0); const normalizeChargingTargetType = useCallback((val?: string | null) => (val ?? '').trim().toLowerCase(), []); @@ -342,16 +342,17 @@ export const CreateManagedControlPlaneWizardContainer: FC - - - - - - -
- - - -
-
- - {/* this condition is to remount the component from scratch to fix a bug with data loading */} - {selectedStep === 'componentSelection' && ( - + {' '} + + + + + + + +
+ + + +
+
+ + {/* this condition is to remount the component from scratch to fix a bug with data loading */} + {selectedStep === 'componentSelection' && ( -
- )} -
- - - - - {isEditMode ? ( - - ) : ( - + + - )} - -
+
+ + {isEditMode ? ( + + ) : ( + + )} + + + ); From f75d6bfdba19d453edb521d530ac8b6305178515 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Goral?= Date: Tue, 23 Sep 2025 14:40:15 +0200 Subject: [PATCH 03/37] fix --- ...eateManagedControlPlaneWizardContainer.tsx | 6 ++-- ...ditManagedControlPlaneWizardDataLoader.tsx | 8 ++++- src/spaces/mcp/pages/McpPage.tsx | 35 +++++++++++++++++-- 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/components/Wizards/CreateManagedControlPlane/CreateManagedControlPlaneWizardContainer.tsx b/src/components/Wizards/CreateManagedControlPlane/CreateManagedControlPlaneWizardContainer.tsx index 416f5ee5..d25c6bd6 100644 --- a/src/components/Wizards/CreateManagedControlPlane/CreateManagedControlPlaneWizardContainer.tsx +++ b/src/components/Wizards/CreateManagedControlPlane/CreateManagedControlPlaneWizardContainer.tsx @@ -67,9 +67,10 @@ type CreateManagedControlPlaneWizardContainerProps = { initialTemplateName?: string; initialData?: ManagedControlPlaneInterface; isOnMcpPage?: boolean; + initialSection?: WizardStepType; }; -type WizardStepType = 'metadata' | 'members' | 'componentSelection' | 'summarize' | 'success'; +export type WizardStepType = 'metadata' | 'members' | 'componentSelection' | 'summarize' | 'success'; const wizardStepOrder: WizardStepType[] = ['metadata', 'members', 'componentSelection', 'summarize', 'success']; @@ -82,12 +83,13 @@ export const CreateManagedControlPlaneWizardContainer: FC { const { t } = useTranslation(); const { user } = useAuthOnboarding(); const errorDialogRef = useRef(null); - const [selectedStep, setSelectedStep] = useState('summarize'); + const [selectedStep, setSelectedStep] = useState(initialSection ?? 'metadata'); const [metadataFormKey, setMetadataFormKey] = useState(0); const normalizeChargingTargetType = useCallback((val?: string | null) => (val ?? '').trim().toLowerCase(), []); diff --git a/src/components/Wizards/CreateManagedControlPlane/EditManagedControlPlaneWizardDataLoader.tsx b/src/components/Wizards/CreateManagedControlPlane/EditManagedControlPlaneWizardDataLoader.tsx index 3c973f9e..60323616 100644 --- a/src/components/Wizards/CreateManagedControlPlane/EditManagedControlPlaneWizardDataLoader.tsx +++ b/src/components/Wizards/CreateManagedControlPlane/EditManagedControlPlaneWizardDataLoader.tsx @@ -3,7 +3,10 @@ import { useApiResource } from '../../../lib/api/useApiResource.ts'; import { ResourceObject } from '../../../lib/api/types/crate/resourceObject.ts'; import styles from './EditManagedControlPlaneWizardDataLoader.module.css'; -import { CreateManagedControlPlaneWizardContainer } from './CreateManagedControlPlaneWizardContainer.tsx'; +import { + CreateManagedControlPlaneWizardContainer, + WizardStepType, +} from './CreateManagedControlPlaneWizardContainer.tsx'; import { PROJECT_NAME_LABEL, WORKSPACE_LABEL } from '../../../lib/api/types/shared/keyNames.ts'; import { BusyIndicator } from '@ui5/webcomponents-react'; @@ -15,6 +18,7 @@ export type EditManagedControlPlaneWizardDataLoaderProps = { isOpen: boolean; setIsOpen: (isOpen: boolean) => void; isOnMcpPage?: boolean; + initialSection?: WizardStepType; }; export const EditManagedControlPlaneWizardDataLoader: FC = ({ @@ -23,6 +27,7 @@ export const EditManagedControlPlaneWizardDataLoader: FC { const { isLoading, data, error } = useApiResource( ResourceObject(workspaceName ?? '', 'managedcontrolplanes', resourceName), @@ -53,6 +58,7 @@ export const EditManagedControlPlaneWizardDataLoader: FC ) : null} diff --git a/src/spaces/mcp/pages/McpPage.tsx b/src/spaces/mcp/pages/McpPage.tsx index 58669c28..8f7c6fb0 100644 --- a/src/spaces/mcp/pages/McpPage.tsx +++ b/src/spaces/mcp/pages/McpPage.tsx @@ -1,4 +1,13 @@ -import { BusyIndicator, ObjectPage, ObjectPageSection, ObjectPageTitle, Panel, Title } from '@ui5/webcomponents-react'; +import { + BusyIndicator, + Button, + FlexBox, + ObjectPage, + ObjectPageSection, + ObjectPageTitle, + Panel, + Title, +} from '@ui5/webcomponents-react'; import { useParams } from 'react-router-dom'; import CopyKubeconfigButton from '../../../components/ControlPlanes/CopyKubeconfigButton.tsx'; import styles from './McpPage.module.css'; @@ -32,17 +41,29 @@ import { useState } from 'react'; import { EditManagedControlPlaneWizardDataLoader } from '../../../components/Wizards/CreateManagedControlPlane/EditManagedControlPlaneWizardDataLoader.tsx'; import { ControlPlanePageMenu } from '../../../components/ControlPlanes/ControlPlanePageMenu.tsx'; import { DISPLAY_NAME_ANNOTATION } from '../../../lib/api/types/shared/keyNames.ts'; +import { WizardStepType } from '../../../components/Wizards/CreateManagedControlPlane/CreateManagedControlPlaneWizardContainer.tsx'; export default function McpPage() { const { projectName, workspaceName, controlPlaneName } = useParams(); const { t } = useTranslation(); const [isEditManagedControlPlaneWizardOpen, setIsEditManagedControlPlaneWizardOpen] = useState(false); + const [editManagedControlPlaneWizardSection, setEditManagedControlPlaneWizardSection] = useState< + undefined | WizardStepType + >(undefined); const { data: mcp, error, isLoading, } = useApiResource(ControlPlaneResource(projectName, workspaceName, controlPlaneName)); const displayName = mcp?.metadata?.annotations?.[DISPLAY_NAME_ANNOTATION]; + const onEditComponents = () => { + setEditManagedControlPlaneWizardSection('componentSelection'); + setIsEditManagedControlPlaneWizardOpen(true); + }; + const handleEditManagedControlPlaneWizardClose = () => { + setIsEditManagedControlPlaneWizardOpen(false); + setEditManagedControlPlaneWizardSection(undefined); + }; if (isLoading) { return ; } @@ -98,10 +119,11 @@ export default function McpPage() { /> } @@ -134,7 +156,14 @@ export default function McpPage() { className={styles.panel} headerLevel="H2" headerText="Panel" - header={{t('McpPage.componentsTitle')}} + header={ + + {t('McpPage.componentsTitle')}{' '} + + + } noAnimation > From 0ff1465cecd4d5d39caca23587fa28723d0d40bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Goral?= Date: Wed, 24 Sep 2025 09:38:18 +0200 Subject: [PATCH 04/37] refactor --- public/locales/en.json | 3 ++- .../ComponentsSelectionContainer.tsx | 4 ++-- .../ComponentsSelectionProvider.tsx | 19 +++++++++++++------ src/spaces/mcp/pages/McpPage.tsx | 5 ++--- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/public/locales/en.json b/public/locales/en.json index 57a90b6f..2a8c785f 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -388,7 +388,8 @@ "editMCP": { "dialogTitle": "Edit Managed Control Plane", "titleText": "Managed Control Plane Updated Successfully!", - "subtitleText": "Your Managed Control Plane is being updated. It will be ready to use in just a few minutes. You can safely close this window." + "subtitleText": "Your Managed Control Plane is being updated. It will be ready to use in just a few minutes. You can safely close this window.", + "editComponents": "Edit components" }, "componentsSelection": { "selectComponents": "Select Components", diff --git a/src/components/ComponentsSelection/ComponentsSelectionContainer.tsx b/src/components/ComponentsSelection/ComponentsSelectionContainer.tsx index 8e55784e..32e143c5 100644 --- a/src/components/ComponentsSelection/ComponentsSelectionContainer.tsx +++ b/src/components/ComponentsSelection/ComponentsSelectionContainer.tsx @@ -29,9 +29,9 @@ export const ComponentsSelectionContainer: React.FC = componentsList, }) => { const { t } = useTranslation(); - const { isLoading, error, templateDefaultsError } = useComponentsSelection(); + const { isLoading, error, templateDefaultsError, hasInitialized } = useComponentsSelection(); - if (isLoading) { + if (isLoading || !hasInitialized) { return ; } diff --git a/src/components/ComponentsSelection/ComponentsSelectionProvider.tsx b/src/components/ComponentsSelection/ComponentsSelectionProvider.tsx index 99675d74..bad0677a 100644 --- a/src/components/ComponentsSelection/ComponentsSelectionProvider.tsx +++ b/src/components/ComponentsSelection/ComponentsSelectionProvider.tsx @@ -20,6 +20,7 @@ export type ComponentsSelectionContextValue = { isLoading: boolean; error: unknown; templateDefaultsError: string | null; + hasInitialized: boolean; }; const ComponentsSelectionContext = createContext(undefined); @@ -69,12 +70,19 @@ export const ComponentsSelectionProvider: React.FC { - const versions = sortVersions(item.status?.versions ?? []); + const rawVersions = Array.isArray(item.status?.versions) ? (item.status?.versions as string[]) : []; + const versions = sortVersions(rawVersions); const template = defaultComponents.find((dc) => dc.name === (item.metadata?.name ?? '')); const templateVersion = template?.version; let selectedVersion = template @@ -112,7 +120,6 @@ export const ComponentsSelectionProvider: React.FC { if (!dc?.name) return; @@ -136,7 +143,6 @@ export const ComponentsSelectionProvider: React.FC 0) return; - const anySelected = componentsList.some((c) => c.isSelected); if (anySelected) return; @@ -144,8 +150,8 @@ export const ComponentsSelectionProvider: React.FC dc.name === c.name); if (!template) return c; const templateVersion = template.version; - const selectedVersion = - templateVersion && Array.isArray(c.versions) && c.versions.includes(templateVersion) ? templateVersion : ''; + const safeVersions = Array.isArray(c.versions) ? c.versions : []; + const selectedVersion = templateVersion && safeVersions.includes(templateVersion) ? templateVersion : ''; return { ...c, isSelected: true, selectedVersion } as ComponentsListItem; }); @@ -157,6 +163,7 @@ export const ComponentsSelectionProvider: React.FC{children}; diff --git a/src/spaces/mcp/pages/McpPage.tsx b/src/spaces/mcp/pages/McpPage.tsx index 8f7c6fb0..dac5e430 100644 --- a/src/spaces/mcp/pages/McpPage.tsx +++ b/src/spaces/mcp/pages/McpPage.tsx @@ -55,6 +55,7 @@ export default function McpPage() { error, isLoading, } = useApiResource(ControlPlaneResource(projectName, workspaceName, controlPlaneName)); + // @ts-ignore const displayName = mcp?.metadata?.annotations?.[DISPLAY_NAME_ANNOTATION]; const onEditComponents = () => { setEditManagedControlPlaneWizardSection('componentSelection'); @@ -159,9 +160,7 @@ export default function McpPage() { header={ {t('McpPage.componentsTitle')}{' '} - + )} @@ -542,7 +543,7 @@ export const CreateManagedControlPlaneWizardContainer: FC -
+
{isDuplicateMode && ( -
+
Date: Fri, 26 Sep 2025 09:23:40 +0200 Subject: [PATCH 34/37] fix --- src/spaces/mcp/pages/McpPage.module.css | 11 +++++++++++ src/spaces/mcp/pages/McpPage.tsx | 11 ++--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/spaces/mcp/pages/McpPage.module.css b/src/spaces/mcp/pages/McpPage.module.css index 4d46900a..f6529c07 100644 --- a/src/spaces/mcp/pages/McpPage.module.css +++ b/src/spaces/mcp/pages/McpPage.module.css @@ -3,3 +3,14 @@ margin: 0.1em auto -8px auto; width: 100%; } + +.actionsBar { + display: flex; + flex-direction: row; + justify-content: space-between; + gap: 0.5rem; +} + +.panelHeader { + width: 100%; +} diff --git a/src/spaces/mcp/pages/McpPage.tsx b/src/spaces/mcp/pages/McpPage.tsx index dac5e430..e2017b30 100644 --- a/src/spaces/mcp/pages/McpPage.tsx +++ b/src/spaces/mcp/pages/McpPage.tsx @@ -95,14 +95,7 @@ export default function McpPage() { breadcrumbs={} //TODO: actionBar should use Toolbar and ToolbarButton for consistent design actionsBar={ -
+
+ {t('McpPage.componentsTitle')}{' '}