diff --git a/src/components/KymaModules/KymaModulesList.js b/src/components/KymaModules/KymaModulesList.js index b03ebc7df9..4f17bf328c 100644 --- a/src/components/KymaModules/KymaModulesList.js +++ b/src/components/KymaModules/KymaModulesList.js @@ -1,4 +1,5 @@ import { useTranslation } from 'react-i18next'; +import { createPortal } from 'react-dom'; import jsyaml from 'js-yaml'; import { ResourceDetails } from 'shared/components/ResourceDetails/ResourceDetails'; @@ -35,6 +36,7 @@ import { Label } from 'shared/ResourceForm/components/Label'; import { cloneDeep } from 'lodash'; import { useCreateResource } from 'shared/ResourceForm/useCreateResource'; import { useNotification } from 'shared/contexts/NotificationContext'; +import { useDeleteResource } from 'shared/hooks/useDeleteResource'; import { PopoverBadge } from 'shared/components/PopoverBadge/PopoverBadge'; import { isFormOpenState } from 'state/formOpenAtom'; @@ -50,6 +52,11 @@ export function KymaModulesList(props) { const setIsFormOpen = useSetRecoilState(isFormOpenState); const { clusterUrl } = useUrl(); + const [DeleteMessageBox, handleResourceDelete] = useDeleteResource({ + resourceType: t('kyma-modules.title'), + forceConfirmDelete: true, + }); + const { data: kymaResources, loading: kymaResourcesLoading } = useGet( '/apis/operator.kyma-project.io/v1beta2/namespaces/kyma-system/kymas', ); @@ -85,6 +92,7 @@ export function KymaModulesList(props) { const { data: crds } = useGet(crdUrl, { pollingInterval: 5000, }); + const [chosenModuleIndex, setChosenModuleIndex] = useState(null); if (kymaResourcesLoading || modulesLoading || kymaResourceLoading) { return ; @@ -123,7 +131,7 @@ export function KymaModulesList(props) { return kymaExt?.find(ext => { const { resource: extensionResource } = jsyaml.load(ext.data.general, { json: true }) || {}; - return extensionResource === resourceKind; + return extensionResource.kind === resourceKind; }); }; const checkBeta = module => { @@ -132,14 +140,7 @@ export function KymaModulesList(props) { ); }; - // TODO: Remove this function and use newfindCrd instead - const findCrd = moduleName => - crds?.items?.find(crd => - crd.metadata.name - .toLocaleLowerCase() - .includes(pluralize(moduleName.replace('-', '').toLocaleLowerCase())), - ); - const newfindCrd = resourceKind => { + const findCrd = resourceKind => { return crds?.items?.find(crd => crd.spec?.names?.kind === resourceKind); }; @@ -162,7 +163,7 @@ export function KymaModulesList(props) { const isError = moduleStatus?.state === 'Error'; const hasExtension = !!findExtension(resource?.resource?.kind); - const hasCrd = !!findCrd(resource.name); + const hasCrd = !!findCrd(resource?.resource?.kind); return ( (isInstalled || isDeletionFailed || !isError) && @@ -205,7 +206,8 @@ export function KymaModulesList(props) { type={ moduleStatus?.state === 'Ready' ? 'Success' - : moduleStatus?.state === 'Processing' + : moduleStatus?.state === 'Processing' || + moduleStatus?.state === 'Deleting' ? 'None' : moduleStatus?.state || 'None' } @@ -219,7 +221,8 @@ export function KymaModulesList(props) { type={ moduleStatus?.state === 'Ready' ? 'Success' - : moduleStatus?.state === 'Processing' + : moduleStatus?.state === 'Processing' || + moduleStatus?.state === 'Deleting' ? 'None' : moduleStatus?.state || 'None' } @@ -284,15 +287,21 @@ export function KymaModulesList(props) { return kymaResourceModule.name === resource.name; }); - selectedModules.splice(index, 1); - setKymaResourceState({ - ...kymaResource, - spec: { - ...kymaResource.spec, - modules: selectedModules, + setChosenModuleIndex(index); + handleResourceDelete({ + deleteFn: () => { + selectedModules.splice(index, 1); + + setKymaResourceState({ + ...kymaResource, + spec: { + ...kymaResource.spec, + modules: selectedModules, + }, + }); + handleModuleUninstall(); }, }); - handleModuleUninstall(); }, }, ]; @@ -300,6 +309,7 @@ export function KymaModulesList(props) { const handleClickResource = (resourceName, resource) => { const isExtension = !!findExtension(resource?.resource?.kind); const moduleStatus = findStatus(resourceName); + const moduleCrd = findCrd(resource?.resource?.kind); const skipRedirect = !hasDetailsLink(resource); if (skipRedirect) { @@ -315,9 +325,7 @@ export function KymaModulesList(props) { ? `${pluralize( moduleStatus?.resource?.kind || '', ).toLowerCase()}/${moduleStatus?.resource?.metadata?.name}` - : `${newfindCrd(resource?.resource?.kind)?.metadata?.name}/${ - moduleStatus?.resource?.metadata?.name - }` + : `${moduleCrd?.metadata?.name}/${moduleStatus?.resource?.metadata?.name}` }`, ) : clusterUrl( @@ -326,16 +334,14 @@ export function KymaModulesList(props) { ? `${pluralize( moduleStatus?.resource?.kind || '', ).toLowerCase()}/${moduleStatus?.resource?.metadata?.name}` - : `${newfindCrd(resource?.resource?.kind)?.metadata?.name}/${ - moduleStatus?.resource?.metadata?.name - }` + : `${moduleCrd?.metadata?.name}/${moduleStatus?.resource?.metadata?.name}` }`, ); if (!isExtension) { setLayoutColumn({ midColumn: { - resourceType: findCrd(resourceName)?.metadata?.name, + resourceType: moduleCrd?.metadata?.name, resourceName: moduleStatus?.resource?.metadata?.name, namespaceId: moduleStatus?.resource?.metadata.namespace || '', }, @@ -369,43 +375,63 @@ export function KymaModulesList(props) { }; return ( - - {t('common.buttons.add')} - , - ]} - customColumnLayout={customColumnLayout} - enableColumnLayout - hasDetailsView - entries={resource?.status?.modules} - headerRenderer={headerRenderer} - rowRenderer={rowRenderer} - noHideFields={['Name', '', 'Namespace']} - displayArrow - title={'Modules'} - sortBy={{ - name: (a, b) => a.name?.localeCompare(b.name), - }} - emptyListProps={{ - image: 'TntComponents', - titleText: `${t('common.labels.no')} ${t( - 'kyma-modules.title', - ).toLocaleLowerCase()}`, - subtitleText: t('kyma-modules.no-modules-description'), - url: - 'https://help.sap.com/docs/btp/sap-business-technology-platform/kyma-s-modular-approach?locale=en-US&state=DRAFT&version=Cloud', - buttonText: t('common.buttons.add'), - showButton: true, - onClick: handleShowAddModule, - }} - /> + <> + {createPortal( + { + selectedModules.splice(chosenModuleIndex, 1); + setKymaResourceState({ + ...kymaResource, + spec: { + ...kymaResource.spec, + modules: selectedModules, + }, + }); + + handleModuleUninstall(); + }} + />, + document.body, + )} + + {t('common.buttons.add')} + , + ]} + customColumnLayout={customColumnLayout} + enableColumnLayout + hasDetailsView + entries={resource?.status?.modules} + headerRenderer={headerRenderer} + rowRenderer={rowRenderer} + noHideFields={['Name', '', 'Namespace']} + displayArrow + title={'Modules'} + sortBy={{ + name: (a, b) => a.name?.localeCompare(b.name), + }} + emptyListProps={{ + image: 'TntComponents', + titleText: `${t('common.labels.no')} ${t( + 'kyma-modules.title', + ).toLocaleLowerCase()}`, + subtitleText: t('kyma-modules.no-modules-description'), + url: + 'https://help.sap.com/docs/btp/sap-business-technology-platform/kyma-s-modular-approach?locale=en-US&state=DRAFT&version=Cloud', + buttonText: t('common.buttons.add'), + showButton: true, + onClick: handleShowAddModule, + }} + /> + ); }; diff --git a/src/shared/hooks/useDeleteResource.js b/src/shared/hooks/useDeleteResource.js index 434404beea..53d928c1d4 100644 --- a/src/shared/hooks/useDeleteResource.js +++ b/src/shared/hooks/useDeleteResource.js @@ -30,6 +30,7 @@ export function useDeleteResource({ layoutNumber, redirectBack = true, parentCrdName, + forceConfirmDelete = false, }) { const { t } = useTranslation(); const [showDeleteDialog, setShowDeleteDialog] = useState(false); @@ -148,7 +149,7 @@ export function useDeleteResource({ }; const handleResourceDelete = ({ resource, resourceUrl, deleteFn }) => { - if (dontConfirmDelete) { + if (dontConfirmDelete && !forceConfirmDelete) { performDelete(resource, resourceUrl, deleteFn); } else { setShowDeleteDialog(true); @@ -218,12 +219,14 @@ export function useDeleteResource({ }, )} - setDontConfirmDelete(prevState => !prevState)} - text={t('common.delete-dialog.delete-confirm')} - /> - {dontConfirmDelete && ( + {!forceConfirmDelete && ( + setDontConfirmDelete(prevState => !prevState)} + text={t('common.delete-dialog.delete-confirm')} + /> + )} + {dontConfirmDelete && !forceConfirmDelete && ( {t('common.delete-dialog.information')}