diff --git a/public/locales/en.json b/public/locales/en.json index 532dc667..1000af79 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -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", @@ -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", @@ -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." } } diff --git a/src/components/ComponentsSelection/ComponentsSelection.module.css b/src/components/ComponentsSelection/ComponentsSelection.module.css new file mode 100644 index 00000000..f8b7ecbf --- /dev/null +++ b/src/components/ComponentsSelection/ComponentsSelection.module.css @@ -0,0 +1,5 @@ +.row { + padding: 1rem; + background: var(--sapBackgroundColor); + border-bottom: 1px solid var(--sapList_BorderColor); +} diff --git a/src/components/ComponentsSelection/ComponentsSelection.tsx b/src/components/ComponentsSelection/ComponentsSelection.tsx new file mode 100644 index 00000000..1abf0d67 --- /dev/null +++ b/src/components/ComponentsSelection/ComponentsSelection.tsx @@ -0,0 +1,155 @@ +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'; +import { ComponentSelectionItem } from '../../lib/api/types/crate/createManagedControlPlane.ts'; + +export interface ComponentsSelectionProps { + components: ComponentSelectionItem[]; + setSelectedComponents: React.Dispatch< + React.SetStateAction + >; +} + +export const ComponentsSelection: React.FC = ({ + components, + setSelectedComponents, +}) => { + const [searchTerm, setSearchTerm] = useState(''); + const { t } = useTranslation(); + const handleSelectionChange = ( + e: Ui5CustomEvent, + ) => { + const id = e.target?.id; + setSelectedComponents((prev) => + prev.map((component) => + component.name === id + ? { ...component, isSelected: !component.isSelected } + : component, + ), + ); + }; + + const handleSearch = (e: Ui5CustomEvent) => { + setSearchTerm(e.target.value.trim()); + }; + + const handleVersionChange = ( + e: Ui5CustomEvent, + ) => { + 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 ( +
+ {t('componentsSelection.selectComponents')} + + } + onInput={handleSearch} + /> + + +
+ {filteredComponents.map((component) => ( + + + + {/*This button will be implemented later*/} + {component.documentationUrl && ( + + )} + + + + ))} +
+
+ {selectedComponents.length > 0 ? ( + + {selectedComponents.map((component) => ( + + ))} + + ) : ( + + {t('componentsSelection.pleaseSelectComponents')} + + )} +
+
+
+ ); +}; diff --git a/src/components/ComponentsSelection/ComponentsSelectionContainer.tsx b/src/components/ComponentsSelection/ComponentsSelectionContainer.tsx new file mode 100644 index 00000000..d489578e --- /dev/null +++ b/src/components/ComponentsSelection/ComponentsSelectionContainer.tsx @@ -0,0 +1,70 @@ +import React, { useEffect, useState } 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 { ComponentSelectionItem } from '../../lib/api/types/crate/createManagedControlPlane.ts'; + +export interface ComponentItem { + name: string; + versions: string[]; +} + +export interface ComponentsSelectionProps { + selectedComponents: ComponentSelectionItem[]; + setSelectedComponents: React.Dispatch< + React.SetStateAction + >; +} +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 ; + } + if (error) return ; + return ( + <> + {selectedComponents.length > 0 ? ( + + ) : ( + + )} + + ); +}; diff --git a/src/components/ControlPlanes/List/ControlPlaneListAllWorkspaces.tsx b/src/components/ControlPlanes/List/ControlPlaneListAllWorkspaces.tsx index a0ee6c31..92a2b0ca 100644 --- a/src/components/ControlPlanes/List/ControlPlaneListAllWorkspaces.tsx +++ b/src/components/ControlPlanes/List/ControlPlaneListAllWorkspaces.tsx @@ -48,7 +48,7 @@ export default function ControlPlaneListAllWorkspaces({ projectName }: Props) { window.open(workspaceCreationGuide, '_blank'); }} > - Help + {t('IllustratedBanner.helpButton')} ) : ( diff --git a/src/components/Members/MemberRoleSelect.tsx b/src/components/Members/MemberRoleSelect.tsx index a170d227..a64c3151 100644 --- a/src/components/Members/MemberRoleSelect.tsx +++ b/src/components/Members/MemberRoleSelect.tsx @@ -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; @@ -36,10 +37,12 @@ export function MemberRoleSelect({ return; } }, [value]); - + const { t } = useTranslation(); return ( - +