Skip to content
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
24b3a83
init
lucasgoral Jun 17, 2025
ad61a55
fixes
lucasgoral Jun 18, 2025
2612811
refactor
lucasgoral Jun 18, 2025
ae5640b
refactor
lucasgoral Jun 20, 2025
8a3ddc6
fixes
lucasgoral Jun 20, 2025
87bc6f8
refactor
lucasgoral Jun 20, 2025
d5fe887
refactor
lucasgoral Jun 20, 2025
6678290
refactor
lucasgoral Jun 23, 2025
5386419
Update ComponentsSelection.tsx
lucasgoral Jun 23, 2025
beca37c
Update ComponentsSelection.tsx
lucasgoral Jun 23, 2025
ca3981c
fixes
lucasgoral Jun 23, 2025
8be43e6
refactor
lucasgoral Jun 23, 2025
1581782
Revert "refactor"
lucasgoral Jun 23, 2025
9688b98
Update CreateManagedControlPlaneWizardContainer.tsx
lucasgoral Jun 23, 2025
fd53f32
refactor
lucasgoral Jun 23, 2025
ce59fc5
Update ComponentsSelectionContainer.tsx
lucasgoral Jun 23, 2025
faca8de
Merge branch 'main' into feat/components-selection-for-mcp
lucasgoral Jun 23, 2025
dbf592d
Update CreateManagedControlPlaneWizardContainer.tsx
lucasgoral Jun 23, 2025
fe31088
fix
lucasgoral Jun 27, 2025
f6a4ca5
fixes
lucasgoral Jun 27, 2025
7f9a96b
fixes
lucasgoral Jun 27, 2025
8e28d80
fix
lucasgoral Jun 27, 2025
d709629
Merge branch 'main' into feat/components-selection-for-mcp
lucasgoral Jun 27, 2025
613e729
Update CreateManagedControlPlaneWizardContainer.tsx
lucasgoral Jun 27, 2025
01339c4
Update ComponentsSelection.module.css
lucasgoral Jun 27, 2025
ab73d54
Update ComponentsSelection.tsx
lucasgoral Jun 27, 2025
58669ee
Update componentsVersions.spec.ts
lucasgoral Jun 27, 2025
85a772b
Update ComponentsSelectionContainer.tsx
lucasgoral Jun 27, 2025
9327fce
Update CreateManagedControlPlaneWizardContainer.tsx
lucasgoral Jun 27, 2025
0f2aef7
Update createManagedControlPlane.ts
lucasgoral Jun 27, 2025
721ce2a
fixes
lucasgoral Jun 27, 2025
a9ef812
fixes
lucasgoral Jun 27, 2025
14a8ece
refactor
lucasgoral Jun 30, 2025
6a2261d
Merge branch 'main' into feat/components-selection-for-mcp
lucasgoral Jul 1, 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
11 changes: 10 additions & 1 deletion public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@
"atLeastOneUser": "You need to have at least one member assigned."
},
"common": {
"documentation": "Documentation",
"close": "Close",
"cannotLoadData": "Cannot load data",
"metadata": "Metadata",
Expand All @@ -281,7 +282,10 @@
"region": "Region",
"success": "Success",
"displayName": "Display Name",
"name": "Name"
"name": "Name",
"componentSelection": "Component Selection",
"search": "Search",
"components": "Components"
},
"buttons": {
"viewResource": "View resource",
Expand All @@ -300,5 +304,10 @@
"dialogTitle": "Create Managed Control Plane",
"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."
},
"componentsSelection": {
"selectComponents": "Select Components",
"selectedComponents": "Selected Components",
"pleaseSelectComponents": "Choose the components you want to add to your Managed Control Plane."
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.row {
padding: 1rem;
background: var(--sapBackgroundColor);
border-bottom: 1px solid var(--sapList_BorderColor);
}
162 changes: 162 additions & 0 deletions src/components/ComponentsSelection/ComponentsSelection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import React, { useState } from 'react';
import {
CheckBox,
Select,
Option,
FlexBox,
Title,
Text,
Input,
Button,
Grid,
List,
ListItemStandard,
Icon,
Ui5CustomEvent,
CheckBoxDomRef,
SelectDomRef,
InputDomRef,
} from '@ui5/webcomponents-react';
import styles from './ComponentsSelection.module.css';
import { Infobox } from '../Ui/Infobox/Infobox.tsx';
import { useTranslation } from 'react-i18next';

export interface ComponentSelectionItem {
name: string;
versions: string[];
isSelected: boolean;
selectedVersion: string;
documentationUrl: string;
}

export interface ComponentsSelectionProps {
components: ComponentSelectionItem[];
setSelectedComponents: React.Dispatch<
React.SetStateAction<ComponentSelectionItem[]>
>;
}

export const ComponentsSelection: React.FC<ComponentsSelectionProps> = ({
components,
setSelectedComponents,
}) => {
const [searchTerm, setSearchTerm] = useState('');
const { t } = useTranslation();
const handleSelectionChange = (
e: Ui5CustomEvent<CheckBoxDomRef, { checked: boolean }>,
) => {
const id = e.target?.id;
setSelectedComponents((prev) =>
prev.map((component) =>
component.name === id
? { ...component, isSelected: !component.isSelected }
: component,
),
);
};

const handleSearch = (e: Ui5CustomEvent<InputDomRef, never>) => {
setSearchTerm(e.target.value.trim());
};

const handleVersionChange = (
e: Ui5CustomEvent<SelectDomRef, { selectedOption: HTMLElement }>,
) => {
const selectedOption = e.detail.selectedOption as HTMLElement;
const name = selectedOption.dataset.name;
const version = selectedOption.dataset.version;
setSelectedComponents((prev) =>
prev.map((component) =>
component.name === name
? { ...component, selectedVersion: version || '' }
: component,
),
);
};

const filteredComponents = components.filter(({ name }) =>
name.includes(searchTerm),
);
const selectedComponents = components.filter(
(component) => component.isSelected,
);

return (
<div>
<Title>{t('componentsSelection.selectComponents')}</Title>

<Input
placeholder={t('common.search')}
id="search"
showClearIcon
icon={<Icon name="search" />}
onInput={handleSearch}
/>

<Grid>
<div data-layout-span="XL8 L8 M8 S8">
{filteredComponents.map((component) => (
<FlexBox
key={component.name}
className={styles.row}
gap={10}
justifyContent="SpaceBetween"
>
<CheckBox
valueState="None"
text={component.name}
id={component.name}
checked={component.isSelected}
onChange={handleSelectionChange}
/>
<FlexBox
gap={10}
justifyContent="SpaceBetween"
alignItems="Baseline"
>
{/*This button will be implemented later*/}
{component.documentationUrl && (
<Button design="Transparent">
{t('common.documentation')}
</Button>
)}
<Select
value={component.selectedVersion}
onChange={handleVersionChange}
>
{component.versions.map((version) => (
<Option
key={version}
data-version={version}
data-name={component.name}
selected={component.selectedVersion === version}
>
{version}
</Option>
))}
</Select>
</FlexBox>
</FlexBox>
))}
</div>
<div data-layout-span="XL4 L4 M4 S4">
{selectedComponents.length > 0 ? (
<List headerText={t('componentsSelection.selectedComponents')}>
{selectedComponents.map((component) => (
<ListItemStandard
key={component.name}
text={component.name}
additionalText={component.selectedVersion}
/>
))}
</List>
) : (
<Infobox fullWidth variant={'success'}>
<Text>{t('componentsSelection.pleaseSelectComponents')}</Text>
</Infobox>
)}
</div>
</Grid>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React, { useEffect, useState } from 'react';
import {
ComponentSelectionItem,
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';

export interface ComponentItem {
name: string;
versions: string[];
}

export interface ComponentsSelectionProps {
selectedComponents: ComponentSelectionItem[];
setSelectedComponents: React.Dispatch<
React.SetStateAction<ComponentSelectionItem[]>
>;
}
export const ComponentsSelectionContainer: React.FC<
ComponentsSelectionProps
> = ({ setSelectedComponents, selectedComponents }) => {
const {
data: allManagedComponents,
error,
isLoading,
} = useApiResource(ListManagedComponents());
const [isReady, setIsReady] = useState(false);
useEffect(() => {
if (
allManagedComponents?.items.length === 0 ||
!allManagedComponents?.items ||
isReady
)
return;

setSelectedComponents(
allManagedComponents?.items?.map((item) => {
const versions = sortVersions(item.status.versions);
return {
name: item.metadata.name,
versions: versions,
selectedVersion: versions[0],
isSelected: false,
documentationUrl: '',
};
}) ?? [],
);
setIsReady(true);
}, [allManagedComponents, isReady, setSelectedComponents]);
if (isLoading) {
return <Loading />;
}
if (error) return <IllustratedError />;
return (
<>
{selectedComponents.length > 0 ? (
<ComponentsSelection
components={selectedComponents}
setSelectedComponents={setSelectedComponents}
/>
) : (
<IllustratedError title={'Cannot load components list'} />
)}
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default function ControlPlaneListAllWorkspaces({ projectName }: Props) {
window.open(workspaceCreationGuide, '_blank');
}}
>
Help
{t('IllustratedBanner.helpButton')}
</Button>
</FlexBox>
) : (
Expand Down
7 changes: 5 additions & 2 deletions src/components/Members/MemberRoleSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from '@ui5/webcomponents-react';
import { SelectChangeEventDetail } from '@ui5/webcomponents/dist/Select.js';
import { useEffect, useRef } from 'react';
import { useTranslation } from 'react-i18next';

interface MemberRoleSelectProps {
value: MemberRoles;
Expand All @@ -36,10 +37,12 @@ export function MemberRoleSelect({
return;
}
}, [value]);

const { t } = useTranslation();
return (
<FlexBox direction={'Column'}>
<Label for={'member-role-select'}>Role</Label>
<Label for={'member-role-select'}>
{t('MemberTable.columnRoleHeader')}
</Label>
<Select
ref={ref}
id="member-role-select"
Expand Down
5 changes: 5 additions & 0 deletions src/components/Ui/Infobox/Infobox.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
margin: 1rem 0;
}

.full-width {
display: block;
margin: 0;
}

.size-sm {
padding: 0.5rem 0.75rem;
font-size: 0.875rem;
Expand Down
3 changes: 3 additions & 0 deletions src/components/Ui/Infobox/Infobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface LabelProps {
size?: 'sm' | 'md' | 'lg';
variant?: 'normal' | 'success' | 'warning' | 'danger';
children: ReactNode;
fullWidth?: boolean;
className?: string;
}

Expand All @@ -16,6 +17,7 @@ export const Infobox: React.FC<LabelProps> = ({
size = 'md', // Default to medium size
variant = 'normal', // Default to normal variant
children,
fullWidth = false,
className,
}) => {
const infoboxClasses = cx(
Expand All @@ -28,6 +30,7 @@ export const Infobox: React.FC<LabelProps> = ({
[styles['variant-success']]: variant === 'success',
[styles['variant-warning']]: variant === 'warning',
[styles['variant-danger']]: variant === 'danger',
[styles['full-width']]: fullWidth,
},
className,
);
Expand Down
Loading
Loading