Skip to content

Commit 7cbf7df

Browse files
authored
Merge pull request #1776 from InsaneZein/delete-workspace-listview
feat(workspaces): add ability to delete workspace from list view
2 parents b942cf0 + 24eaf3f commit 7cbf7df

File tree

2 files changed

+104
-23
lines changed

2 files changed

+104
-23
lines changed

src/Messages.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -2613,13 +2613,13 @@ export default defineMessages({
26132613
id: 'deleteWorkspaceModalBody',
26142614
description: 'Modal body text for deleting a workspace',
26152615
defaultMessage:
2616-
'<b>{name}</b> and all its data will be permanently deleted. All access granted to user groups via this workspace will be removed.',
2616+
'{count, plural, one {<b>{name}</b> workspace and all its} other {<b>{count} workspaces</b> and all their}} data will be permanently deleted. All access granted to user groups via this workspace will be removed.',
26172617
},
26182618
workspaceNotEmptyWarning: {
26192619
id: 'workspaceNotEmptyWarning',
26202620
description: 'Display text in delete modal when workspace is not empty',
26212621
defaultMessage:
2622-
'Workspace must be empty to delete and must not have any children workspaces. You must move assets in this workspace to other workspaces in order to proceed.',
2622+
'{count, plural, one {Workspace} other {Workspaces}} must be empty to delete and must not have any children workspaces. You must move assets in {count, plural, one {this workspace} other {these workspaces}} to other workspaces in order to proceed.',
26232623
},
26242624
gotItButtonLabel: {
26252625
id: 'gotItButtonLabel',

src/smart-components/workspaces/WorkspaceListTable.tsx

+102-21
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
ResponsiveActions,
1212
SkeletonTableBody,
1313
SkeletonTableHead,
14+
WarningModal,
1415
} from '@patternfly/react-component-groups';
1516
import {
1617
DataView,
@@ -22,15 +23,17 @@ import {
2223
DataViewToolbar,
2324
DataViewTrTree,
2425
useDataViewSelection,
26+
DataViewTrObject,
2527
} from '@patternfly/react-data-view';
2628
import { Workspace } from '../../redux/reducers/workspaces-reducer';
2729
import { RBACStore } from '../../redux/store';
2830
import AppLink from '../../presentational-components/shared/AppLink';
2931
import pathnames from '../../utilities/pathnames';
3032
import messages from '../../Messages';
3133
import useAppNavigate from '../../hooks/useAppNavigate';
32-
import { EmptyState, EmptyStateHeader, EmptyStateIcon, EmptyStateBody } from '@patternfly/react-core';
34+
import { EmptyState, EmptyStateHeader, EmptyStateIcon, EmptyStateBody, ButtonVariant } from '@patternfly/react-core';
3335
import { SearchIcon } from '@patternfly/react-icons';
36+
import { ActionsColumn } from '@patternfly/react-table';
3437

3538
interface WorkspaceFilters {
3639
name: string;
@@ -50,26 +53,6 @@ const mapWorkspacesToHierarchy = (workspaceData: Workspace[]): Workspace | undef
5053
return root;
5154
};
5255

53-
const buildRows = (workspaces: Workspace[]): DataViewTrTree[] =>
54-
workspaces.map((workspace) => ({
55-
row: [
56-
<AppLink
57-
to={pathnames['workspace-detail'].link.replace(':workspaceId', workspace.id)}
58-
key={`${workspace.id}-detail`}
59-
className="rbac-m-hide-on-sm"
60-
>
61-
{workspace.name}
62-
</AppLink>,
63-
workspace.description,
64-
],
65-
id: workspace.id,
66-
...(workspace.children && workspace.children.length > 0
67-
? {
68-
children: buildRows(workspace.children),
69-
}
70-
: {}),
71-
}));
72-
7356
const EmptyWorkspacesTable: React.FunctionComponent<{ titleText: string }> = ({ titleText }) => {
7457
return (
7558
<EmptyState>
@@ -116,6 +99,50 @@ const WorkspaceListTable = () => {
11699
const dispatch = useDispatch();
117100
const navigate = useAppNavigate();
118101
const selection = useDataViewSelection({ matchOption: (a, b) => a.id === b.id });
102+
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
103+
const [currentWorkspaces, setCurrentWorkspaces] = useState<Workspace[]>([]);
104+
105+
const handleModalToggle = (workspaces: Workspace[]) => {
106+
setCurrentWorkspaces(workspaces);
107+
setIsDeleteModalOpen(!isDeleteModalOpen);
108+
};
109+
110+
const buildRows = (workspaces: Workspace[]): DataViewTrTree[] =>
111+
workspaces.map((workspace) => ({
112+
row: Object.values({
113+
name: (
114+
<AppLink
115+
to={pathnames['workspace-detail'].link.replace(':workspaceId', workspace.id)}
116+
key={`${workspace.id}-detail`}
117+
className="rbac-m-hide-on-sm"
118+
>
119+
{workspace.name}
120+
</AppLink>
121+
),
122+
description: workspace.description,
123+
rowActions: {
124+
cell: (
125+
<ActionsColumn
126+
items={[
127+
{
128+
title: 'Delete workspace',
129+
onClick: () => {
130+
handleModalToggle([workspace]);
131+
},
132+
},
133+
]}
134+
/>
135+
),
136+
props: { isActionCell: true },
137+
},
138+
}),
139+
id: workspace.id,
140+
...(workspace.children && workspace.children.length > 0
141+
? {
142+
children: buildRows(workspace.children),
143+
}
144+
: {}),
145+
}));
119146

120147
const { isLoading, workspaces, error } = useSelector((state: RBACStore) => ({
121148
workspaces: state.workspacesReducer.workspaces || [],
@@ -152,12 +179,55 @@ const WorkspaceListTable = () => {
152179
selection.onSelect(value === BulkSelectValue.all, value === BulkSelectValue.all ? workspaces : []);
153180
};
154181

182+
const hasAssets = useMemo(() => {
183+
return selection.selected.filter((ws) => ws.children && ws.children?.length > 0).length > 0 ? true : false;
184+
}, [selection.selected, workspaces]);
185+
155186
if (error) {
156187
return <ErrorState errorDescription={error} />;
157188
}
158189

159190
return (
160191
<React.Fragment>
192+
{isDeleteModalOpen && (
193+
<WarningModal
194+
ouiaId={'remove-workspaces-modal'}
195+
isOpen={isDeleteModalOpen}
196+
title={intl.formatMessage(messages.deleteWorkspaceModalHeader)}
197+
confirmButtonLabel={!hasAssets ? intl.formatMessage(messages.delete) : intl.formatMessage(messages.gotItButtonLabel)}
198+
confirmButtonVariant={!hasAssets ? ButtonVariant.danger : ButtonVariant.primary}
199+
withCheckbox={!hasAssets}
200+
checkboxLabel={intl.formatMessage(messages.understandActionIrreversible)}
201+
onClose={() => setIsDeleteModalOpen(false)}
202+
onConfirm={() => {
203+
!hasAssets ? console.log('deleting workspaces') : null;
204+
setIsDeleteModalOpen(false);
205+
}}
206+
cancelButtonLabel={!hasAssets ? 'Cancel' : ''}
207+
>
208+
{hasAssets ? (
209+
<FormattedMessage
210+
{...messages.workspaceNotEmptyWarning}
211+
values={{
212+
b: (text) => <b>{text}</b>,
213+
count: currentWorkspaces.length,
214+
plural: currentWorkspaces.length > 1 ? intl.formatMessage(messages.workspaces) : intl.formatMessage(messages.workspace),
215+
name: currentWorkspaces[0]?.name,
216+
}}
217+
/>
218+
) : (
219+
<FormattedMessage
220+
{...messages.deleteWorkspaceModalBody}
221+
values={{
222+
b: (text) => <b>{text}</b>,
223+
count: currentWorkspaces.length,
224+
plural: currentWorkspaces.length > 1 ? intl.formatMessage(messages.workspaces) : intl.formatMessage(messages.workspace),
225+
name: currentWorkspaces[0]?.name,
226+
}}
227+
/>
228+
)}
229+
</WarningModal>
230+
)}
161231
<DataView selection={selection} activeState={activeState}>
162232
<DataViewToolbar
163233
bulkSelect={
@@ -187,6 +257,17 @@ const WorkspaceListTable = () => {
187257
<ResponsiveAction ouiaId="create-workspace-button" isPinned onClick={() => navigate({ pathname: pathnames['create-workspace'].link })}>
188258
{intl.formatMessage(messages.createWorkspace)}
189259
</ResponsiveAction>
260+
<ResponsiveAction
261+
ouiaId="delete-workspace-button"
262+
isDisabled={selection.selected.length === 0}
263+
onClick={() => {
264+
handleModalToggle(
265+
workspaces.filter((workspace) => selection.selected.some((selectedRow: DataViewTrObject) => selectedRow.id === workspace.id))
266+
);
267+
}}
268+
>
269+
{intl.formatMessage(messages.workspacesActionDeleteWorkspace)}
270+
</ResponsiveAction>
190271
</ResponsiveActions>
191272
}
192273
/>

0 commit comments

Comments
 (0)