Skip to content

Commit 635024b

Browse files
committed
Refactor delete namespace modal
1 parent cc8b702 commit 635024b

File tree

6 files changed

+123
-81
lines changed

6 files changed

+123
-81
lines changed

frontend/packages/console-app/console-extensions.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,6 +1338,17 @@
13381338
"provider": { "$codeRef": "defaultProvider.useDefaultActionsProvider" }
13391339
}
13401340
},
1341+
{
1342+
"type": "console.action/resource-provider",
1343+
"properties": {
1344+
"model": {
1345+
"group": "",
1346+
"version": "v1",
1347+
"kind": "Namespace"
1348+
},
1349+
"provider": { "$codeRef": "projectProvider.useNamespaceActionsProvider" }
1350+
}
1351+
},
13411352
{
13421353
"type": "console.navigation/resource-ns",
13431354
"properties": {
Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,65 @@
11
import { useMemo } from 'react';
2+
import { useTranslation } from 'react-i18next';
23
import { useK8sModel } from '@console/dynamic-plugin-sdk/src/lib-core';
3-
import { K8sResourceKind, referenceFor } from '@console/internal/module/k8s';
4+
import { useDeleteNamespaceModalLauncher } from '@console/internal/components/modals/delete-namespace-modal';
5+
import { asAccessReview } from '@console/internal/components/utils/rbac';
6+
import { K8sModel, K8sResourceKind, referenceFor } from '@console/internal/module/k8s';
47
import { CommonActionCreator } from '../hooks/types';
58
import { useCommonActions } from '../hooks/useCommonActions';
69

10+
const useDeleteAction = (kindObj: K8sModel, resource: K8sResourceKind) => {
11+
const { t } = useTranslation();
12+
const launchDeleteModal = useDeleteNamespaceModalLauncher({ kind: kindObj, resource });
13+
const hidden = resource.metadata.name === 'default' || resource.status?.phase === 'Terminating';
14+
15+
const factory = useMemo(
16+
() => ({
17+
delete: () => ({
18+
id: 'delete-project',
19+
label: t('console-app~Delete Project'),
20+
cta: launchDeleteModal,
21+
accessReview: asAccessReview(kindObj, resource, 'delete'),
22+
}),
23+
}),
24+
// eslint-disable-next-line react-hooks/exhaustive-deps
25+
[kindObj, resource, t],
26+
);
27+
const action = useMemo(() => (!hidden ? [factory.delete()] : []), [factory, hidden]);
28+
return action;
29+
};
30+
731
export const useProjectActionsProvider = (resource: K8sResourceKind) => {
832
const [kindObj, inFlight] = useK8sModel(referenceFor(resource));
9-
const [actions, isReady] = useCommonActions(kindObj, resource, [
33+
const [editAction, isReady] = useCommonActions(kindObj, resource, [
1034
CommonActionCreator.Edit,
11-
CommonActionCreator.Delete,
1235
] as const);
13-
const projectActions = useMemo(() => (isReady ? Object.values(actions) : []), [actions, isReady]);
36+
const deleteAction = useDeleteAction(kindObj, resource);
37+
const projectActions = useMemo(() => (isReady ? Object.values(editAction) : []), [
38+
editAction,
39+
isReady,
40+
]);
41+
const actions = useMemo(() => [...projectActions, ...deleteAction], [
42+
projectActions,
43+
deleteAction,
44+
]);
45+
return [actions, !inFlight, false];
46+
};
1447

15-
return [projectActions, !inFlight, false];
48+
export const useNamespaceActionsProvider = (resource: K8sResourceKind) => {
49+
const [kindObj, inFlight] = useK8sModel(referenceFor(resource));
50+
const [commonActions, isReady] = useCommonActions(kindObj, resource, [
51+
CommonActionCreator.ModifyLabels,
52+
CommonActionCreator.ModifyAnnotations,
53+
CommonActionCreator.Edit,
54+
] as const);
55+
const deleteAction = useDeleteAction(kindObj, resource);
56+
const namespaceActions = useMemo(() => (isReady ? Object.values(commonActions) : []), [
57+
commonActions,
58+
isReady,
59+
]);
60+
const actions = useMemo(() => [...namespaceActions, ...deleteAction], [
61+
namespaceActions,
62+
deleteAction,
63+
]);
64+
return [actions, !inFlight, false];
1665
};

frontend/packages/dev-console/src/components/projects/details/ProjectDetailsPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const ProjectDetails = (props) => {
4040
]}
4141
name={activeNamespace}
4242
kind={referenceForModel(ProjectModel)}
43-
customActionMenu={(obj) => (
43+
customActionMenu={(k8sObj, obj) => (
4444
<LazyActionMenu
4545
context={{ [referenceForModel(ProjectModel)]: obj }}
4646
variant={ActionMenuVariant.DROPDOWN}

frontend/public/components/modals/delete-namespace-modal.tsx

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,31 @@ import { useDispatch, useSelector } from 'react-redux';
44
import { useNavigate } from 'react-router-dom-v5-compat';
55
import { RootState } from '@console/internal/redux';
66
import { k8sKill, K8sKind, K8sResourceKind } from '@console/internal/module/k8s';
7-
import {
8-
createModalLauncher,
9-
ModalTitle,
10-
ModalBody,
11-
ModalSubmitFooter,
12-
ModalComponentProps,
13-
} from '@console/internal/components/factory/modal';
7+
import { ModalComponentProps } from '@console/internal/components/factory/modal';
148
import {
159
ALL_NAMESPACES_KEY,
1610
LAST_NAMESPACE_NAME_LOCAL_STORAGE_KEY,
1711
LAST_NAMESPACE_NAME_USER_SETTINGS_KEY,
1812
} from '@console/shared/src/constants/common';
1913
import { useUserSettingsCompatibility } from '@console/shared/src/hooks/useUserSettingsCompatibility';
20-
import { YellowExclamationTriangleIcon } from '@console/shared/src/components/status/icons';
2114
import { usePromiseHandler } from '@console/shared/src/hooks/promise-handler';
2215
import { getActiveNamespace } from '../../reducers/ui';
2316
import { setActiveNamespace, formatNamespaceRoute } from '../../actions/ui';
17+
import {
18+
Button,
19+
Modal,
20+
ModalHeader,
21+
ModalVariant,
22+
ModalBody,
23+
ModalFooter,
24+
} from '@patternfly/react-core';
25+
import { OverlayComponent, useOverlay } from '@console/dynamic-plugin-sdk/src/lib-core';
26+
import { ErrorMessage } from '../utils/button-bar';
2427

25-
export const DeleteNamespaceModal: React.FC<DeleteNamespaceModalProps> = ({
26-
cancel,
27-
close,
28+
export const DeleteNamespaceModal: OverlayComponent<DeleteNamespaceModalProps> = ({
2829
kind,
2930
resource,
31+
closeOverlay,
3032
}) => {
3133
const navigate = useNavigate();
3234
const { t } = useTranslation();
@@ -59,7 +61,7 @@ export const DeleteNamespaceModal: React.FC<DeleteNamespaceModalProps> = ({
5961
dispatch(setActiveNamespace(ALL_NAMESPACES_KEY));
6062
setLastNamespace(ALL_NAMESPACES_KEY);
6163
}
62-
close?.();
64+
closeOverlay();
6365
navigate(`/k8s/cluster/${kind.plural}`);
6466
})
6567
.catch(() => {
@@ -72,20 +74,20 @@ export const DeleteNamespaceModal: React.FC<DeleteNamespaceModalProps> = ({
7274
};
7375

7476
return (
75-
<form onSubmit={onSubmit} name="form" className="modal-content">
76-
<ModalTitle className="modal-header">
77-
<YellowExclamationTriangleIcon className="co-icon-space-r" />{' '}
78-
{t('public~Delete {{label}}?', { label: t(kind.labelKey) })}
79-
</ModalTitle>
77+
<Modal isOpen onClose={closeOverlay} variant={ModalVariant.small}>
78+
<ModalHeader
79+
title={t('public~Delete {{label}}?', { label: t(kind.labelKey) })}
80+
titleIconVariant="warning"
81+
/>
8082
<ModalBody>
81-
<p>
83+
<p className="pf-v6-u-mb-md">
8284
<Trans t={t} ns="public">
8385
This action cannot be undone. It will destroy all pods, services and other objects in
8486
the namespace{' '}
8587
<strong className="co-break-word">{{ name: resource.metadata.name }}</strong>.
8688
</Trans>
8789
</p>
88-
<p>
90+
<p className="pf-v6-u-mb-md">
8991
<Trans t={t} ns="public">
9092
Confirm deletion by typing{' '}
9193
<strong className="co-break-word">{{ name: resource.metadata.name }}</strong> below:
@@ -104,19 +106,26 @@ export const DeleteNamespaceModal: React.FC<DeleteNamespaceModalProps> = ({
104106
/>
105107
</span>
106108
</ModalBody>
107-
<ModalSubmitFooter
108-
submitText={t('public~Delete')}
109-
submitDisabled={!confirmed}
110-
cancel={() => cancel?.()}
111-
errorMessage={errorMessage}
112-
inProgress={inProgress}
113-
submitDanger
114-
/>
115-
</form>
109+
<ModalFooter>
110+
{errorMessage && <ErrorMessage message={errorMessage} />}
111+
<Button variant="danger" onClick={onSubmit} isLoading={inProgress} isDisabled={!confirmed}>
112+
{t('public~Delete')}
113+
</Button>
114+
<Button variant="secondary" onClick={closeOverlay}>
115+
{t('public~Cancel')}
116+
</Button>
117+
</ModalFooter>
118+
</Modal>
116119
);
117120
};
118121

119-
export const deleteNamespaceModal = createModalLauncher(DeleteNamespaceModal);
122+
export const useDeleteNamespaceModalLauncher = (props: DeleteNamespaceModalProps) => {
123+
const launcher = useOverlay();
124+
return React.useCallback(() => launcher<DeleteNamespaceModalProps>(DeleteNamespaceModal, props), [
125+
launcher,
126+
props,
127+
]);
128+
};
120129

121130
type DeleteNamespaceModalProps = {
122131
resource: K8sResourceKind;

frontend/public/components/modals/index.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,6 @@ export const confirmModal = (props) =>
1515
m.confirmModal(props),
1616
);
1717

18-
export const deleteNamespaceModal = (props) =>
19-
import('./delete-namespace-modal' /* webpackChunkName: "delete-namespace-modal" */).then((m) =>
20-
m.deleteNamespaceModal(props),
21-
);
22-
2318
/** @deprecated Use useErrorModalLauncher hook instead */
2419
export const errorModal = (props) =>
2520
import('./error-modal' /* webpackChunkName: "error-modal" */).then((m) => m.errorModal(props));

frontend/public/components/namespace.jsx

Lines changed: 19 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {
1111
DescriptionListDescription,
1212
DescriptionListGroup,
1313
DescriptionListTerm,
14-
Tooltip,
1514
Grid,
1615
GridItem,
1716
} from '@patternfly/react-core';
@@ -58,7 +57,6 @@ import { DetailsPage, ListPage, sorts } from './factory';
5857
import { sortResourceByValue } from './factory/Table/sort';
5958
import { ExternalLink } from '@console/shared/src/components/links/ExternalLink';
6059
import { DetailsItem } from './utils/details-item';
61-
import { Kebab, ResourceKebab } from './utils/kebab';
6260
import { LabelList } from './utils/label-list';
6361
import { LoadingInline, LoadingBox } from './utils/status-box';
6462
import { ResourceIcon } from './utils/resource-icon';
@@ -74,7 +72,7 @@ import {
7472
import { navFactory } from './utils/horizontal-nav';
7573
import { useAccessReview } from './utils/rbac';
7674
import { Timestamp } from '@console/shared/src/components/datetime/Timestamp';
77-
import { deleteNamespaceModal, configureNamespacePullSecretModal } from './modals';
75+
import { configureNamespacePullSecretModal } from './modals';
7876
import { RoleBindingsPage } from './RBAC';
7977
import { Bar } from './graphs/bar';
8078
import { Area } from './graphs/area';
@@ -104,6 +102,8 @@ import {
104102
} from '@console/app/src/components/data-view/ConsoleDataView';
105103
import { DataViewCheckboxFilter } from '@patternfly/react-data-view';
106104
import { getGroupVersionKindForModel } from '@console/dynamic-plugin-sdk/src/utils/k8s/k8s-ref';
105+
import LazyActionMenu from '@console/shared/src/components/actions/LazyActionMenu';
106+
import { ActionMenuVariant } from '@console/shared/src/components/actions/types';
107107

108108
const getDisplayName = (obj) =>
109109
_.get(obj, ['metadata', 'annotations', 'openshift.io/display-name']);
@@ -134,38 +134,6 @@ const getFilters = () => [
134134
},
135135
];
136136

137-
export const deleteModal = (kind, ns) => {
138-
const { labelKey, labelKind, weight, accessReview } = Kebab.factory.Delete(kind, ns);
139-
let callback = undefined;
140-
let tooltip;
141-
let label;
142-
143-
if (ns.metadata.name === 'default') {
144-
tooltip = `${kind.label} default cannot be deleted`;
145-
} else if (ns.status?.phase === 'Terminating') {
146-
tooltip = `${kind.label} is already terminating`;
147-
} else {
148-
callback = () => deleteNamespaceModal({ kind, resource: ns });
149-
}
150-
if (tooltip) {
151-
label = (
152-
<div className="dropdown__disabled">
153-
<Tooltip content={tooltip}>
154-
<span>{i18next.t(labelKey, labelKind)}</span>
155-
</Tooltip>
156-
</div>
157-
);
158-
}
159-
return { label, labelKey, labelKind, weight, callback, accessReview };
160-
};
161-
162-
const nsMenuActions = [
163-
Kebab.factory.ModifyLabels,
164-
Kebab.factory.ModifyAnnotations,
165-
Kebab.factory.Edit,
166-
deleteModal,
167-
];
168-
169137
const fetchNamespaceMetrics = () => {
170138
const metrics = [
171139
{
@@ -359,7 +327,7 @@ const getNamespaceDataViewRows = (rowData, tableColumns, namespaceMetrics, t) =>
359327
cell: <LabelList kind="Namespace" labels={labels} />,
360328
},
361329
[namespaceColumnInfo[9].id]: {
362-
cell: <ResourceKebab actions={nsMenuActions} kind="Namespace" resource={ns} />,
330+
cell: <LazyActionMenu context={{ [referenceForModel(NamespaceModel)]: ns }} />,
363331
props: actionsCellProps,
364332
},
365333
};
@@ -489,8 +457,6 @@ export const NamespacesPage = (props) => {
489457
);
490458
};
491459

492-
export const projectMenuActions = [Kebab.factory.Edit, deleteModal];
493-
494460
const projectColumnManagementID = referenceForModel(ProjectModel);
495461

496462
// Use same column info as namespaces since projects are namespaces
@@ -671,7 +637,7 @@ const getProjectDataViewRows = (
671637
cell: <LabelList labels={labels} kind="Project" />,
672638
},
673639
[projectColumnInfo[9].id]: {
674-
cell: <ResourceKebab actions={projectMenuActions} kind="Project" resource={project} />,
640+
cell: <LazyActionMenu context={{ [referenceForModel(ProjectModel)]: project }} />,
675641
props: actionsCellProps,
676642
},
677643
};
@@ -1125,7 +1091,13 @@ const RolesPage = ({ obj: { metadata } }) => {
11251091
export const NamespacesDetailsPage = (props) => (
11261092
<DetailsPage
11271093
{...props}
1128-
menuActions={nsMenuActions}
1094+
kind={referenceForModel(NamespaceModel)}
1095+
customActionMenu={(k8sObj, obj) => (
1096+
<LazyActionMenu
1097+
context={{ [referenceForModel(NamespaceModel)]: obj }}
1098+
variant={ActionMenuVariant.DROPDOWN}
1099+
/>
1100+
)}
11291101
pages={[
11301102
navFactory.details(NamespaceDetails),
11311103
navFactory.editYaml(),
@@ -1138,7 +1110,13 @@ export const ProjectsDetailsPage = (props) => {
11381110
return (
11391111
<DetailsPage
11401112
{...props}
1141-
menuActions={projectMenuActions}
1113+
kind={referenceForModel(ProjectModel)}
1114+
customActionMenu={(k8sObj, obj) => (
1115+
<LazyActionMenu
1116+
context={{ [referenceForModel(ProjectModel)]: obj }}
1117+
variant={ActionMenuVariant.DROPDOWN}
1118+
/>
1119+
)}
11421120
pages={[
11431121
{
11441122
href: '',

0 commit comments

Comments
 (0)