Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 22 additions & 0 deletions frontend/packages/console-app/console-extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -1338,6 +1338,28 @@
"provider": { "$codeRef": "defaultProvider.useDefaultActionsProvider" }
}
},
{
"type": "console.action/resource-provider",
"properties": {
"model": {
"group": "project.openshift.io",
"version": "v1",
"kind": "Project"
},
"provider": { "$codeRef": "projectProvider.useProjectActionsProvider" }
}
},
{
"type": "console.action/resource-provider",
"properties": {
"model": {
"group": "",
"version": "v1",
"kind": "Namespace"
},
"provider": { "$codeRef": "projectProvider.useNamespaceActionsProvider" }
}
},
{
"type": "console.navigation/resource-ns",
"properties": {
Expand Down
1 change: 1 addition & 0 deletions frontend/packages/console-app/locales/en/console-app.json
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@
"Restore as new PVC": "Restore as new PVC",
"Volume Snapshot is not Ready": "Volume Snapshot is not Ready",
"View instances": "View instances",
"Delete {{label}}": "Delete {{label}}",
"Current default StorageClass": "Current default StorageClass",
"Set as default": "Set as default",
"Access mode": "Access mode",
Expand Down
1 change: 1 addition & 0 deletions frontend/packages/console-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"perspectiveStateProvider": "src/actions/providers/perspective-state-provider.ts",
"bindingProvider": "src/actions/providers/binding-provider.ts",
"buildProvider": "src/actions/providers/build-provider.ts",
"projectProvider": "src/actions/providers/project-provider.ts",
"ClusterConfigurationPage": "src/components/cluster-configuration/ClusterConfigurationPage.tsx",
"PreferredPerspectiveSelect": "src/components/user-preferences/perspective/PreferredPerspectiveSelect.tsx",
"NamespaceDropdown": "src/components/user-preferences/namespace/NamespaceDropdown.tsx",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useK8sModel } from '@console/dynamic-plugin-sdk/src/lib-core';
import { useDeleteNamespaceModalLauncher } from '@console/internal/components/modals/delete-namespace-modal';
import { asAccessReview } from '@console/internal/components/utils/rbac';
import { K8sModel, K8sResourceKind, referenceFor } from '@console/internal/module/k8s';
import { CommonActionCreator } from '../hooks/types';
import { useCommonActions } from '../hooks/useCommonActions';

const useDeleteAction = (kindObj: K8sModel, resource: K8sResourceKind) => {
const { t } = useTranslation();
const launchDeleteModal = useDeleteNamespaceModalLauncher({ kind: kindObj, resource });
const hidden = resource.metadata.name === 'default' || resource.status?.phase === 'Terminating';

const factory = useMemo(
() => ({
delete: () => ({
id: 'delete-project',
label: t('console-app~Delete {{label}}', { label: t(kindObj.labelKey) }),
cta: launchDeleteModal,
accessReview: asAccessReview(kindObj, resource, 'delete'),
}),
}),
// missing launchDeleteModal dependency, that causes max depth exceeded error
// eslint-disable-next-line react-hooks/exhaustive-deps
[kindObj, resource, t],
);
const action = useMemo(() => (!hidden ? [factory.delete()] : []), [factory, hidden]);
return action;
};

export const useProjectActionsProvider = (resource: K8sResourceKind) => {
const [kindObj, inFlight] = useK8sModel(referenceFor(resource));
const [editAction, isReady] = useCommonActions(kindObj, resource, [
CommonActionCreator.Edit,
] as const);
const deleteAction = useDeleteAction(kindObj, resource);
const projectActions = useMemo(() => (isReady ? Object.values(editAction) : []), [
editAction,
isReady,
]);
const actions = useMemo(() => [...projectActions, ...deleteAction], [
projectActions,
deleteAction,
]);
return [actions, !inFlight, false];
};

export const useNamespaceActionsProvider = (resource: K8sResourceKind) => {
const [kindObj, inFlight] = useK8sModel(referenceFor(resource));
const [commonActions, isReady] = useCommonActions(kindObj, resource, [
CommonActionCreator.ModifyLabels,
CommonActionCreator.ModifyAnnotations,
CommonActionCreator.Edit,
] as const);
const deleteAction = useDeleteAction(kindObj, resource);
const namespaceActions = useMemo(() => (isReady ? Object.values(commonActions) : []), [
commonActions,
isReady,
]);
const actions = useMemo(() => [...namespaceActions, ...deleteAction], [
namespaceActions,
deleteAction,
]);
return [actions, !inFlight, false];
};
5 changes: 3 additions & 2 deletions frontend/packages/dev-console/locales/en/devconsole.json
Original file line number Diff line number Diff line change
Expand Up @@ -655,10 +655,11 @@
"Chosen Cluster Roles": "Chosen Cluster Roles",
" or <1>create a Project</1>": " or <1>create a Project</1>",
" or <1>create a Namespace</1>": " or <1>create a Namespace</1>",
"Overview": "Overview",
"Details": "Details",
"Projects": "Projects",
"Project Details": "Project Details",
"Actions": "Actions",
"Overview": "Overview",
"Details": "Details",
"Select a Project to view its details<1></1>.": "Select a Project to view its details<1></1>.",
"{{kindForRefPlural}} in {{apiVersionForRefPlural}}": "{{kindForRefPlural}} in {{apiVersionForRefPlural}}",
"The server doesn't have a resource type {{missingType}}. Try refreshing the page if it was recently added.": "The server doesn't have a resource type {{missingType}}. Try refreshing the page if it was recently added.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ import { Trans, useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom-v5-compat';
import { ProjectDashboard } from '@console/internal/components/dashboard/project-dashboard/project-dashboard';
import { DetailsPage } from '@console/internal/components/factory';
import { NamespaceDetails, projectMenuActions } from '@console/internal/components/namespace';
import { NamespaceDetails } from '@console/internal/components/namespace';
import { withStartGuide } from '@console/internal/components/start-guide';
import { history, useAccessReview, Page } from '@console/internal/components/utils';
import { ProjectModel, RoleBindingModel } from '@console/internal/models';
import { ALL_NAMESPACES_KEY } from '@console/shared';
import { referenceForModel } from '@console/internal/module/k8s';
import LazyActionMenu from '@console/shared/src/components/actions/LazyActionMenu';
import { ActionMenuVariant } from '@console/shared/src/components/actions/types';
import { DocumentTitle } from '@console/shared/src/components/document-title/DocumentTitle';
import { ALL_NAMESPACES_KEY } from '@console/shared/src/constants';
import NamespacedPage, { NamespacedPageVariants } from '../../NamespacedPage';
import ProjectAccessPage from '../../project-access/ProjectAccessPage';
import CreateProjectListPage, { CreateAProjectButton } from '../CreateProjectListPage';
Expand All @@ -25,6 +28,32 @@ const handleNamespaceChange = (newNamespace: string): void => {
}
};

const ProjectDetails = (props) => {
const { t } = useTranslation();
const { activeNamespace, pages } = props;
return (
<DetailsPage
{...props}
breadcrumbsFor={() => [
{ name: t('devconsole~Projects'), path: '/project-details/all-namespaces' },
{ name: t('devconsole~Project Details'), path: `/project-details/ns/${activeNamespace}` },
]}
name={activeNamespace}
kind={referenceForModel(ProjectModel)}
customActionMenu={(k8sObj, obj) => (
<LazyActionMenu
context={{ [referenceForModel(ProjectModel)]: obj }}
variant={ActionMenuVariant.DROPDOWN}
label={t('devconsole~Actions')}
/>
)}
kindObj={ProjectModel}
customData={{ activeNamespace, hideHeading: true }}
pages={pages}
/>
);
};

export const PageContents: React.FC<MonitoringPageProps> = ({ noProjectsAvailable, ...props }) => {
const { t } = useTranslation();
const params = useParams();
Expand Down Expand Up @@ -68,19 +97,7 @@ export const PageContents: React.FC<MonitoringPageProps> = ({ noProjectsAvailabl
}

return !noProjectsAvailable && activeNamespace ? (
<DetailsPage
{...props}
breadcrumbsFor={() => [
{ name: t('devconsole~Projects'), path: '/project-details/all-namespaces' },
{ name: t('devconsole~Project Details'), path: `/project-details/ns/${activeNamespace}` },
]}
name={activeNamespace}
kind={ProjectModel.kind}
kindObj={ProjectModel}
menuActions={projectMenuActions}
customData={{ activeNamespace, hideHeading: true }}
pages={pages}
/>
<ProjectDetails {...props} activeNamespace={activeNamespace} pages={pages} />
) : (
<CreateProjectListPage title={t('devconsole~Project Details')}>
{(openProjectModal) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ jest.mock('@console/internal/components/utils/rbac', () => ({
useAccessReview: jest.fn(),
}));

jest.mock('@console/internal/module/k8s', () => ({
referenceForModel: jest.fn(),
}));

jest.mock('@console/internal/models', () => ({
ProjectModel: { kind: 'Project' },
RoleBindingModel: {
Expand Down
1 change: 0 additions & 1 deletion frontend/packages/topology/src/actions/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export * from './edgeActions';
export * from './nodeActions';
export * from './modify-application';
export * from './contextMenuActions';
export * from './provider';
13 changes: 0 additions & 13 deletions frontend/packages/topology/src/actions/nodeActions.ts

This file was deleted.

73 changes: 42 additions & 31 deletions frontend/public/components/modals/delete-namespace-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,33 @@ import { useDispatch, useSelector } from 'react-redux';
import { useNavigate } from 'react-router-dom-v5-compat';
import { RootState } from '@console/internal/redux';
import { k8sKill, K8sKind, K8sResourceKind } from '@console/internal/module/k8s';
import {
createModalLauncher,
ModalTitle,
ModalBody,
ModalSubmitFooter,
ModalComponentProps,
} from '@console/internal/components/factory/modal';
import { ModalComponentProps } from '@console/internal/components/factory/modal';
import {
ALL_NAMESPACES_KEY,
LAST_NAMESPACE_NAME_LOCAL_STORAGE_KEY,
LAST_NAMESPACE_NAME_USER_SETTINGS_KEY,
} from '@console/shared/src/constants/common';
import { useUserSettingsCompatibility } from '@console/shared/src/hooks/useUserSettingsCompatibility';
import { YellowExclamationTriangleIcon } from '@console/shared/src/components/status/icons';
import { usePromiseHandler } from '@console/shared/src/hooks/promise-handler';
import { getActiveNamespace } from '../../reducers/ui';
import { setActiveNamespace, formatNamespaceRoute } from '../../actions/ui';
import {
Button,
Modal,
ModalHeader,
ModalVariant,
ModalBody,
ModalFooter,
Content,
ContentVariants,
} from '@patternfly/react-core';
import { OverlayComponent, useOverlay } from '@console/dynamic-plugin-sdk/src/lib-core';
import { ErrorMessage } from '../utils/button-bar';

export const DeleteNamespaceModal: React.FC<DeleteNamespaceModalProps> = ({
cancel,
close,
export const DeleteNamespaceModal: OverlayComponent<DeleteNamespaceModalProps> = ({
kind,
resource,
closeOverlay,
}) => {
const navigate = useNavigate();
const { t } = useTranslation();
Expand Down Expand Up @@ -59,7 +63,7 @@ export const DeleteNamespaceModal: React.FC<DeleteNamespaceModalProps> = ({
dispatch(setActiveNamespace(ALL_NAMESPACES_KEY));
setLastNamespace(ALL_NAMESPACES_KEY);
}
close?.();
closeOverlay();
navigate(`/k8s/cluster/${kind.plural}`);
})
.catch(() => {
Expand All @@ -72,25 +76,25 @@ export const DeleteNamespaceModal: React.FC<DeleteNamespaceModalProps> = ({
};

return (
<form onSubmit={onSubmit} name="form" className="modal-content">
<ModalTitle className="modal-header">
<YellowExclamationTriangleIcon className="co-icon-space-r" />{' '}
{t('public~Delete {{label}}?', { label: t(kind.labelKey) })}
</ModalTitle>
<Modal isOpen onClose={closeOverlay} variant={ModalVariant.small}>
<ModalHeader
title={t('public~Delete {{label}}?', { label: t(kind.labelKey) })}
titleIconVariant="warning"
/>
<ModalBody>
<p>
<Content component={ContentVariants.p}>
<Trans t={t} ns="public">
This action cannot be undone. It will destroy all pods, services and other objects in
the namespace{' '}
<strong className="co-break-word">{{ name: resource.metadata.name }}</strong>.
</Trans>
</p>
<p>
</Content>
<Content component={ContentVariants.p}>
<Trans t={t} ns="public">
Confirm deletion by typing{' '}
<strong className="co-break-word">{{ name: resource.metadata.name }}</strong> below:
</Trans>
</p>
</Content>
<span className="pf-v6-c-form-control">
<input
type="text"
Expand All @@ -104,19 +108,26 @@ export const DeleteNamespaceModal: React.FC<DeleteNamespaceModalProps> = ({
/>
</span>
</ModalBody>
<ModalSubmitFooter
submitText={t('public~Delete')}
submitDisabled={!confirmed}
cancel={() => cancel?.()}
errorMessage={errorMessage}
inProgress={inProgress}
submitDanger
/>
</form>
<ModalFooter>
{errorMessage && <ErrorMessage message={errorMessage} />}
<Button variant="danger" onClick={onSubmit} isLoading={inProgress} isDisabled={!confirmed}>
{t('public~Delete')}
</Button>
<Button variant="secondary" onClick={closeOverlay}>
{t('public~Cancel')}
</Button>
</ModalFooter>
</Modal>
);
};

export const deleteNamespaceModal = createModalLauncher(DeleteNamespaceModal);
export const useDeleteNamespaceModalLauncher = (props: DeleteNamespaceModalProps) => {
const launcher = useOverlay();
return React.useCallback(() => launcher<DeleteNamespaceModalProps>(DeleteNamespaceModal, props), [
launcher,
props,
]);
};

type DeleteNamespaceModalProps = {
resource: K8sResourceKind;
Expand Down
5 changes: 0 additions & 5 deletions frontend/public/components/modals/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@ export const confirmModal = (props) =>
m.confirmModal(props),
);

export const deleteNamespaceModal = (props) =>
import('./delete-namespace-modal' /* webpackChunkName: "delete-namespace-modal" */).then((m) =>
m.deleteNamespaceModal(props),
);

/** @deprecated Use useErrorModalLauncher hook instead */
export const errorModal = (props) =>
import('./error-modal' /* webpackChunkName: "error-modal" */).then((m) => m.errorModal(props));
Expand Down
Loading