Skip to content

Commit

Permalink
frontend: allow multiple deletes on ResourceTable
Browse files Browse the repository at this point in the history
* allow rowselection
* externalize logic

folllow up:
* support more actions such as restarts
* support auth check
* support plugin extensions

Signed-off-by: farodin91 <[email protected]>
  • Loading branch information
farodin91 committed Nov 21, 2024
1 parent fe20e5d commit e150cb4
Show file tree
Hide file tree
Showing 42 changed files with 4,647 additions and 694 deletions.
4 changes: 2 additions & 2 deletions frontend/src/components/common/ConfirmDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import MuiDialog, { DialogProps as MuiDialogProps } from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogContentText from '@mui/material/DialogContentText';
import React from 'react';
import React, { ReactNode } from 'react';
import { useTranslation } from 'react-i18next';
import { DialogTitle } from './Dialog';

export interface ConfirmDialogProps extends MuiDialogProps {
title: string;
description: string;
description: string | ReactNode;
onConfirm: () => void;
handleClose: () => void;
}
Expand Down
109 changes: 109 additions & 0 deletions frontend/src/components/common/Resource/DeleteMultipleButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux';
import { KubeObject } from '../../../lib/k8s/KubeObject';
import { CallbackActionOptions, clusterAction } from '../../../redux/clusterActionSlice';
import {
EventStatus,
HeadlampEventType,
useEventCallback,
} from '../../../redux/headlampEventSlice';
import { AppDispatch } from '../../../redux/stores/store';
import ActionButton, { ButtonStyle } from '../ActionButton';
import { ConfirmDialog } from '../Dialog';

interface DeleteMultipleButtonProps {
items?: KubeObject[];
options?: CallbackActionOptions;
buttonStyle?: ButtonStyle;
afterConfirm?: () => void;
}

interface DeleteMultipleButtonDescriptionProps {
items?: KubeObject[];
}

function DeleteMultipleButtonDescription(props: DeleteMultipleButtonDescriptionProps) {
const { t } = useTranslation(['translation']);
return (
<p>
{t('Are you sure you want to delete following items?')}
<ul>
{props.items?.map(item => (
<li>{item.metadata.name}</li>
))}
</ul>
</p>
);
}

export default function DeleteMultipleButton(props: DeleteMultipleButtonProps) {
const dispatch: AppDispatch = useDispatch();
const { items, options, afterConfirm, buttonStyle } = props;
const [openAlert, setOpenAlert] = React.useState(false);
const { t } = useTranslation(['translation']);
const dispatchDeleteEvent = useEventCallback(HeadlampEventType.DELETE_RESOURCE);

const deleteFunc = React.useCallback(
(item: KubeObject) => {
if (!item) {
return;
}

const callback = item!.delete;

const itemName = item!.metadata.name;

callback &&
dispatch(
clusterAction(callback.bind(item), {
startMessage: t('Deleting item {{ itemName }}…', { itemName }),
cancelledMessage: t('Cancelled deletion of {{ itemName }}.', { itemName }),
successMessage: t('Deleted item {{ itemName }}.', { itemName }),
errorMessage: t('Error deleting item {{ itemName }}.', { itemName }),
cancelUrl: location.pathname,
startUrl: item!.getListLink(),
errorUrl: item!.getListLink(),
...options,
})
);
},
// eslint-disable-next-line
[items]
);

if (!items || items.length === 0) {
return null;
}

return (
<>
<ActionButton
description={t('translation|Delete items')}
buttonStyle={buttonStyle}
onClick={() => {
setOpenAlert(true);
}}
icon="mdi:delete"
/>
<ConfirmDialog
open={openAlert}
title={t('translation|Delete items')}
description={<DeleteMultipleButtonDescription items={items} />}
handleClose={() => setOpenAlert(false)}
onConfirm={() => {
items.forEach(item => {
deleteFunc(item);
dispatchDeleteEvent({
resource: item,
status: EventStatus.CONFIRMED,
});
});
if (afterConfirm) {
afterConfirm();
}
}}
/>
</>
);
}
11 changes: 10 additions & 1 deletion frontend/src/components/common/Resource/ResourceTable.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { MenuItem, TableCellProps } from '@mui/material';
import { useTheme } from '@mui/material/styles';
import { MRT_FilterFns, MRT_Row, MRT_SortingFn } from 'material-react-table';
import { MRT_FilterFns, MRT_Row, MRT_SortingFn, MRT_TableInstance } from 'material-react-table';
import { ComponentProps, ReactNode, useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import helpers from '../../../helpers';
Expand All @@ -19,6 +19,7 @@ import Link from '../Link';
import Table, { TableColumn } from '../Table';
import DeleteButton from './DeleteButton';
import EditButton from './EditButton';
import ResourceTableMultiActions from './ResourecTableMultiActions';
import { RestartButton } from './RestartButton';
import ScaleButton from './ScaleButton';
import ViewButton from './ViewButton';
Expand Down Expand Up @@ -458,6 +459,12 @@ function ResourceTableContent<RowItem extends KubeObject>(props: ResourceTablePr
};
}, [actionsProcessed]);

const renderRowSelectionToolbar = useMemo(() => {
return ({ table }: { table: MRT_TableInstance<Record<string, any>> }) => (
<ResourceTableMultiActions table={table} />
);
}, []);

function onColumnsVisibilityChange(updater: any): void {
setColumnVisibility(oldCols => {
const newCols = updater(oldCols);
Expand Down Expand Up @@ -490,6 +497,8 @@ function ResourceTableContent<RowItem extends KubeObject>(props: ResourceTablePr
<Table
enableFullScreenToggle={false}
enableFacetedValues
enableRowSelection
renderRowSelectionToolbar={renderRowSelectionToolbar}
errorMessage={errorMessage}
// @todo: once KubeObject is not any we can remove this casting
columns={allColumns as TableColumn<Record<string, any>>[]}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { MRT_TableInstance } from 'material-react-table';
import { useCallback } from 'react';
import { KubeObject } from '../../../lib/k8s/KubeObject';
import DeleteMultipleButton from './DeleteMultipleButton';

export interface ResourceTableMultiActionsProps<RowItem extends Record<string, any>> {
table: MRT_TableInstance<RowItem>;
}

export default function ResourceTableMultiActions<RowItem extends Record<string, any>>(
props: ResourceTableMultiActionsProps<RowItem>
) {
const { table } = props;
const items = table.getSelectedRowModel().rows.map(t => t.original as unknown as KubeObject);

const afterConfirm = useCallback(() => {
table.resetRowSelection();
}, [table]);

return <DeleteMultipleButton items={items} afterConfirm={afterConfirm} />;
}
Loading

0 comments on commit e150cb4

Please sign in to comment.