Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
23c23d7
init
lucasgoral Aug 26, 2025
0c01a1a
Update ImportMembersDialog.tsx
lucasgoral Aug 27, 2025
6b440c0
updates
lucasgoral Aug 27, 2025
72ab035
Update ImportMembersDialog.tsx
lucasgoral Aug 27, 2025
d2ce22a
Update ImportMembersDialog.tsx
lucasgoral Aug 27, 2025
166f647
Update ImportMembersDialog.tsx
lucasgoral Aug 27, 2025
4a4dcc3
Update ImportMembersDialog.tsx
lucasgoral Aug 27, 2025
2931141
fixes
lucasgoral Aug 28, 2025
590a1fb
fixes
lucasgoral Aug 28, 2025
9b833dc
Update MemberTable.tsx
lucasgoral Aug 28, 2025
db1afff
Merge branch 'main' into import-members
lucasgoral Aug 28, 2025
421477e
Merge branch 'members-table-fix' into import-members
lucasgoral Aug 28, 2025
ba36f94
Update ImportMembersDialog.tsx
lucasgoral Aug 28, 2025
74b192d
refactor
lucasgoral Aug 28, 2025
402c410
fix
lucasgoral Aug 28, 2025
ccdd7a5
Update ImportMembersDialog.tsx
lucasgoral Aug 28, 2025
78a6fab
Merge branch 'main' into import-members
lucasgoral Aug 29, 2025
0ae01d9
Update ImportMembersDialog.tsx
lucasgoral Aug 29, 2025
3dd5531
Update ImportMembersDialog.tsx
lucasgoral Aug 29, 2025
6044067
fixes
lucasgoral Aug 29, 2025
781b873
fix
lucasgoral Aug 29, 2025
f62e443
suggest label and icon
GenosseOtt Aug 29, 2025
9511fdd
simplify members table
GenosseOtt Aug 29, 2025
4100f11
fixes
lucasgoral Aug 29, 2025
da3fdec
fix
lucasgoral Aug 29, 2025
e4b7dd4
Update MemberTable.tsx
lucasgoral Aug 29, 2025
63f7c3c
Merge branch 'main' into import-members
Lasserich Aug 29, 2025
0a17041
Merge branch 'main' into import-members
andreaskienle Sep 3, 2025
b47494d
Revamp Import Members dialog UI and logic
andreaskienle Sep 4, 2025
d4d3401
Merge branch 'main' into import-members
andreaskienle Sep 4, 2025
3aa41c9
Implement review feedback
andreaskienle Sep 4, 2025
503af2a
Add toast
andreaskienle Sep 4, 2025
e8b65f7
Fix linting
andreaskienle Sep 4, 2025
471e21d
init
lucasgoral Sep 9, 2025
ba662db
fixes
lucasgoral Sep 9, 2025
7febbfa
fix
lucasgoral Sep 9, 2025
089f965
fix
lucasgoral Sep 9, 2025
5df9fbb
Update CreateManagedControlPlaneWizardContainer.tsx
lucasgoral Sep 10, 2025
3a0307b
Merge branch 'import-members' into edit-mcp
lucasgoral Sep 10, 2025
14e76b8
Update CreateManagedControlPlaneWizardContainer.tsx
lucasgoral Sep 10, 2025
bba4c9e
Merge branch 'main' into edit-mcp
lucasgoral Sep 10, 2025
cad8b2c
Update createManagedControlPlane.ts
lucasgoral Sep 11, 2025
c88c362
fixes
lucasgoral Sep 11, 2025
37140a5
fix
lucasgoral Sep 11, 2025
86913d6
Update CreateManagedControlPlaneWizardContainer.tsx
lucasgoral Sep 11, 2025
62ac31e
fixes
lucasgoral Sep 11, 2025
2939393
fixes
lucasgoral Sep 12, 2025
94f247a
fix
lucasgoral Sep 12, 2025
2ecb4a8
fix
lucasgoral Sep 12, 2025
a4ddcc2
Update CreateManagedControlPlaneWizardContainer.tsx
lucasgoral Sep 12, 2025
0743aa5
Update EditManagedControlPlaneWizardDataLoader.tsx
lucasgoral Sep 12, 2025
1b3f3de
fixes
lucasgoral Sep 15, 2025
a7594fb
fix
lucasgoral Sep 15, 2025
8680b16
Update ComponentsSelectionContainer.tsx
lucasgoral Sep 15, 2025
9600aa1
Update CreateManagedControlPlaneWizardContainer.tsx
lucasgoral Sep 15, 2025
7ecae3e
Update CreateManagedControlPlaneWizardContainer.tsx
lucasgoral Sep 15, 2025
3f11aa4
Merge branch 'main' into edit-mcp
lucasgoral Sep 15, 2025
442575b
fixes
lucasgoral Sep 15, 2025
bd20053
fixes
lucasgoral Sep 15, 2025
0fbe5b3
Update CreateManagedControlPlaneWizardContainer.tsx
lucasgoral Sep 15, 2025
7ff45f5
reafctor
lucasgoral Sep 15, 2025
4ad9b4d
fix
lucasgoral Sep 16, 2025
d0484aa
fixes
lucasgoral Sep 16, 2025
873b75c
Merge branch 'main' into edit-mcp
lucasgoral Sep 16, 2025
d0dfd22
Update CreateManagedControlPlaneWizardContainer.tsx
lucasgoral Sep 16, 2025
2291c56
Update CreateManagedControlPlaneWizardContainer.tsx
lucasgoral Sep 16, 2025
dbce6af
Update ComponentsSelectionContainer.tsx
lucasgoral Sep 16, 2025
3e33016
fix
lucasgoral Sep 17, 2025
e9d5c2d
fixes
lucasgoral Sep 17, 2025
6ce7759
fix
lucasgoral Sep 18, 2025
6653985
fix
lucasgoral Sep 18, 2025
70e8bc0
Update CreateManagedControlPlaneWizardContainer.tsx
lucasgoral Sep 18, 2025
9fe2de1
fix
lucasgoral Sep 18, 2025
74a7a94
fix
lucasgoral Sep 19, 2025
04b696d
Merge branch 'main' into edit-mcp
lucasgoral Sep 19, 2025
da39fd6
Update YamlDiff.module.css
lucasgoral Sep 19, 2025
8fb3105
Update YamlViewer.tsx
lucasgoral Sep 19, 2025
2469b93
fix
lucasgoral Sep 19, 2025
bd623a5
fix
lucasgoral Sep 19, 2025
39e81cc
Update src/components/Wizards/CreateManagedControlPlane/CreateManaged…
lucasgoral Sep 19, 2025
3710cae
Update src/components/Wizards/CreateManagedControlPlane/CreateManaged…
lucasgoral Sep 19, 2025
05dc482
Update src/components/ComponentsSelection/ComponentsSelectionContaine…
lucasgoral Sep 19, 2025
ee76d3d
Update ControlPlaneCard.tsx
lucasgoral Sep 19, 2025
9883a24
fix
lucasgoral Sep 19, 2025
e58125b
Update ControlPlaneCard.tsx
lucasgoral Sep 19, 2025
49882c1
Update MetadataForm.tsx
lucasgoral Sep 22, 2025
cd1dc0d
Update MetadataForm.tsx
lucasgoral Sep 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"@xyflow/react": "12.8.4",
"clsx": "2.1.1",
"dagre": "0.8.5",
"diff": "^8.0.2",
"dotenv": "17.2.2",
"fastify": "5.6.0",
"fastify-plugin": "5.0.1",
Expand Down
13 changes: 11 additions & 2 deletions public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@
"loadingErrorMessage": "Failed to list mcps in workspace"
},
"ControlPlaneCard": {
"deleteConfirmationDialog": "MCP deletion triggered. The list will refresh automatically once completed."
"deleteConfirmationDialog": "MCP deletion triggered. The list will refresh automatically once completed.",
"editMCP": "Edit Managed Control Plane",
"deleteMCP": "Delete Managed Control Plane"

},
"ControlPlaneListAllWorkspaces": {
"emptyListTitleMessage": "No Workspaces created yet",
Expand Down Expand Up @@ -370,7 +373,8 @@
"create": "Create",
"close": "Close",
"back": "Back",
"cancel": "Cancel"
"cancel": "Cancel",
"update": "Update"
},
"yaml": {
"copiedToClipboard": "YAML copied to clipboard!",
Expand All @@ -381,6 +385,11 @@
"titleText": "Managed Control Plane Created Successfully!",
"subtitleText": "Your Managed Control Plane is being set up. It will be ready to use in just a few minutes. You can safely close this window."
},
"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."
},
"componentsSelection": {
"selectComponents": "Select Components",
"selectedComponents": "Selected Components",
Expand Down
122 changes: 82 additions & 40 deletions src/components/ComponentsSelection/ComponentsSelectionContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useMemo, useRef, useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { ComponentsSelection } from './ComponentsSelection.tsx';

import IllustratedError from '../Shared/IllustratedError.tsx';
Expand All @@ -14,7 +14,11 @@ import { ManagedControlPlaneTemplate } from '../../lib/api/types/templates/mcpTe
export interface ComponentsSelectionProps {
componentsList: ComponentsListItem[];
setComponentsList: (components: ComponentsListItem[]) => void;
setInitialComponentsList: (components: ComponentsListItem[]) => void;
managedControlPlaneTemplate?: ManagedControlPlaneTemplate;
initialSelection?: Record<string, { isSelected: boolean; version: string }>;
isOnMcpPage?: boolean;
initializedComponents: React.RefObject<boolean>;
}

/**
Expand All @@ -25,9 +29,7 @@ export const getSelectedComponents = (components: ComponentsListItem[]) => {
const isCrossplaneSelected = components.some(({ name, isSelected }) => name === 'crossplane' && isSelected);
return components.filter((component) => {
if (!component.isSelected) return false;
if (component.name?.includes('provider') && !isCrossplaneSelected) {
return false;
}
if (component.name?.includes('provider') && !isCrossplaneSelected) return false;
return true;
});
};
Expand All @@ -43,71 +45,112 @@ export const ComponentsSelectionContainer: React.FC<ComponentsSelectionProps> =
setComponentsList,
componentsList,
managedControlPlaneTemplate,
initialSelection,
isOnMcpPage,
setInitialComponentsList,
initializedComponents,
}) => {
const { data: availableManagedComponentsListData, error, isLoading } = useApiResource(ListManagedComponents());
const {
data: availableManagedComponentsListData,
error,
isLoading,
} = useApiResource(ListManagedComponents(), undefined, !!isOnMcpPage);
const { t } = useTranslation();
const initialized = useRef(false);

const [templateDefaultsError, setTemplateDefaultsError] = useState<string | null>(null);
const defaultComponents = useMemo<TemplateDefaultComponent[]>(
() => managedControlPlaneTemplate?.spec?.spec?.components?.defaultComponents ?? [],
[managedControlPlaneTemplate],
);

useEffect(() => {
const items = availableManagedComponentsListData?.items ?? [];

if (!items.length) {
if (!initialized.current) return;
setTemplateDefaultsError(null);
if (
initializedComponents.current ||
!availableManagedComponentsListData?.items ||
availableManagedComponentsListData.items.length === 0
) {
return;
}

if (!initialized.current) {
const newComponentsList = items
.map((item) => {
const versions = sortVersions(item.status.versions);
const template = defaultComponents.find((dc) => dc.name === item.metadata.name);
const templateVersion = template?.version;
const selectedVersion = template
? templateVersion && versions.includes(templateVersion)
? templateVersion
: ''
: (versions[0] ?? '');
return {
name: item.metadata.name,
versions,
selectedVersion,
isSelected: !!template,
documentationUrl: '',
};
})
.filter((component) => !removeComponents.find((item) => item === component.name));

setComponentsList(newComponentsList);
initialized.current = true;
}
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]);

if (!defaultComponents.length) {
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);
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 : [];
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, setComponentsList]);
}, [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]);

if (isLoading) {
return <Loading />;
Expand All @@ -117,7 +160,6 @@ export const ComponentsSelectionContainer: React.FC<ComponentsSelectionProps> =
return <IllustratedError compact={true} />;
}

// Defensive: If the API returned no items, show error
if (!componentsList || componentsList.length === 0) {
return <IllustratedError title={t('componentsSelection.cannotLoad')} compact={true} />;
}
Expand Down
31 changes: 21 additions & 10 deletions src/components/ControlPlanes/ControlPlaneCard/ControlPlaneCard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Button, Card, FlexBox, Label, Title } from '@ui5/webcomponents-react';
import { Card, FlexBox, Label, Title } from '@ui5/webcomponents-react';
import '@ui5/webcomponents-fiori/dist/illustrations/NoData.js';
import '@ui5/webcomponents-fiori/dist/illustrations/EmptyList.js';
import '@ui5/webcomponents-icons/dist/delete';
Expand Down Expand Up @@ -27,6 +27,11 @@ import { useToast } from '../../../context/ToastContext.tsx';
import { canConnectToMCP } from '../controlPlanes.ts';
import { Infobox } from '../../Ui/Infobox/Infobox.tsx';

import { ControlPlaneCardMenu } from './ControlPlaneCardMenu.tsx';

import { EditManagedControlPlaneWizardDataLoader } from '../../Wizards/CreateManagedControlPlane/EditManagedControlPlaneWizardDataLoader.tsx';
import { DISPLAY_NAME_ANNOTATION } from '../../../lib/api/types/shared/keyNames.ts';

interface Props {
controlPlane: ListControlPlanesType;
workspace: ListWorkspacesType;
Expand All @@ -37,7 +42,7 @@ export function ControlPlaneCard({ controlPlane, workspace, projectName }: Props
const [dialogDeleteMcpIsOpen, setDialogDeleteMcpIsOpen] = useState(false);
const toast = useToast();
const { t } = useTranslation();

const [isEditManagedControlPlaneWizardOpen, setIsEditManagedControlPlaneWizardOpen] = useState(false);
const { trigger: patchTrigger } = useApiResourceMutation<DeleteMCPType>(
PatchMCPResourceForDeletion(controlPlane.metadata.namespace, controlPlane.metadata.name),
);
Expand All @@ -46,6 +51,9 @@ export function ControlPlaneCard({ controlPlane, workspace, projectName }: Props
);

const name = controlPlane.metadata.name;
const displayName =
controlPlane?.metadata?.annotations?.[DISPLAY_NAME_ANNOTATION as keyof typeof controlPlane.metadata.annotations];

const namespace = controlPlane.metadata.namespace;

const isSystemIdentityProviderEnabled = Boolean(controlPlane.spec?.authentication?.enableSystemIdentityProvider);
Expand All @@ -61,7 +69,7 @@ export function ControlPlaneCard({ controlPlane, workspace, projectName }: Props
<FlexBox direction="Column">
<FlexBox direction="Row" justifyContent="SpaceBetween">
<FlexBox direction="Column">
<Title level={TitleLevel.H5}>{name}</Title>
<Title level={TitleLevel.H5}>{displayName ? displayName : name}</Title>
<Label>{workspace.metadata.name} </Label>
</FlexBox>
<div>
Expand All @@ -74,13 +82,10 @@ export function ControlPlaneCard({ controlPlane, workspace, projectName }: Props
</div>
</FlexBox>
<FlexBox direction="Row" justifyContent="SpaceBetween" alignItems="Center" className={styles.row}>
<Button
design={'Transparent'}
icon="delete"
disabled={controlPlane.status?.status === ReadyStatus.InDeletion}
onClick={() => {
setDialogDeleteMcpIsOpen(true);
}}
<ControlPlaneCardMenu
setDialogDeleteMcpIsOpen={setDialogDeleteMcpIsOpen}
isDeleteMcpButtonDisabled={controlPlane.status?.status === ReadyStatus.InDeletion}
setIsEditManagedControlPlaneWizardOpen={setIsEditManagedControlPlaneWizardOpen}
/>
<FlexBox direction="Row" justifyContent="SpaceBetween" alignItems="Center" gap={10}>
<YamlViewButtonWithLoader
Expand Down Expand Up @@ -124,6 +129,12 @@ export function ControlPlaneCard({ controlPlane, workspace, projectName }: Props
toast.show(t('ControlPlaneCard.deleteConfirmationDialog'));
}}
/>
<EditManagedControlPlaneWizardDataLoader
isOpen={isEditManagedControlPlaneWizardOpen}
setIsOpen={setIsEditManagedControlPlaneWizardOpen}
workspaceName={namespace}
resourceName={name}
/>
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Button, Menu, MenuItem } from '@ui5/webcomponents-react';

import { Dispatch, FC, SetStateAction, useRef, useState } from 'react';
import '@ui5/webcomponents-icons/dist/copy';
import '@ui5/webcomponents-icons/dist/accept';

import { useTranslation } from 'react-i18next';

type ControlPlanesListMenuProps = {
setDialogDeleteMcpIsOpen: Dispatch<SetStateAction<boolean>>;
isDeleteMcpButtonDisabled: boolean;
setIsEditManagedControlPlaneWizardOpen: Dispatch<SetStateAction<boolean>>;
};

export const ControlPlaneCardMenu: FC<ControlPlanesListMenuProps> = ({
setDialogDeleteMcpIsOpen,
isDeleteMcpButtonDisabled,
setIsEditManagedControlPlaneWizardOpen,
}) => {
const buttonRef = useRef(null);
const [menuIsOpen, setMenuIsOpen] = useState(false);
const { t } = useTranslation();

const handleOpenerClick = () => {
setMenuIsOpen(true);
};

return (
<>
<Button ref={buttonRef} icon="overflow" icon-end onClick={handleOpenerClick} />
<Menu
open={menuIsOpen}
opener={buttonRef.current}
onItemClick={(event) => {
const action = (event.detail.item as HTMLElement).dataset.action;
if (action === 'editMcp') {
setIsEditManagedControlPlaneWizardOpen(true);
}
if (action === 'deleteMcp') {
setDialogDeleteMcpIsOpen(true);
}

setMenuIsOpen(false);
}}
onClose={() => {
setMenuIsOpen(false);
}}
>
<MenuItem
key={'delete'}
text={t('ControlPlaneCard.deleteMCP')}
data-action="deleteMcp"
icon="delete"
disabled={isDeleteMcpButtonDisabled}
/>
<MenuItem
key={'edit'}
text={t('ControlPlaneCard.editMCP')}
data-action="editMcp"
icon="edit"
disabled={isDeleteMcpButtonDisabled}
/>
</Menu>
</>
);
};
Loading
Loading