Skip to content

Commit

Permalink
[Feature] It's not possible to select and delete volumes #1998 (#2000)
Browse files Browse the repository at this point in the history
  • Loading branch information
olgenn authored Nov 14, 2024
1 parent ff42647 commit b236b8e
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 10 deletions.
5 changes: 4 additions & 1 deletion frontend/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,11 @@ export const API = {
POOLS: (projectName: IProject['project_name']) => `${API.BASE()}/project/${projectName}/pool/list`,
POOL_DETAILS: (projectName: IProject['project_name']) => `${API.BASE()}/project/${projectName}/pool/show`,

// Pools
// Fleets
FLEETS: (projectName: IProject['project_name']) => `${API.BASE()}/project/${projectName}/fleets/list`,

// Fleets
VOLUMES_DELETE: (projectName: IProject['project_name']) => `${API.BASE()}/project/${projectName}/volumes/delete`,
},

BACKENDS: {
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,8 @@
"empty_message_text": "No volumes to display.",
"nomatch_message_title": "No matches",
"nomatch_message_text": "We can't find a match.",
"delete_volumes_confirm_title": "Delete volumes",
"delete_volumes_confirm_message": "Are you sure you want to delete these volumes?",
"active_only": "Active volumes",

"name": "Name",
Expand Down
63 changes: 60 additions & 3 deletions frontend/src/pages/Volumes/List/hooks.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { format } from 'date-fns';

Expand All @@ -9,7 +9,9 @@ import { DATE_TIME_FORMAT, DEFAULT_TABLE_PAGE_SIZE } from 'consts';
import { useLocalStorageState } from 'hooks/useLocalStorageState';
import { getStatusIconType } from 'libs/volumes';
import { useGetProjectsQuery } from 'services/project';
import { useLazyGetAllVolumesQuery } from 'services/volume';
import { useDeleteVolumesMutation, useLazyGetAllVolumesQuery } from 'services/volume';

import { useNotifications } from '../../../hooks';

export const useVolumesTableEmptyMessages = ({
clearFilters,
Expand Down Expand Up @@ -100,10 +102,13 @@ export const useVolumesData = ({ project_name, only_active }: TVolumesListReques
const [data, setData] = useState<IVolume[]>([]);
const [pagesCount, setPagesCount] = useState<number>(1);
const [disabledNext, setDisabledNext] = useState(false);
const lastRequestParams = useRef<TVolumesListRequestParams | undefined>(undefined);

const [getVolumes, { isLoading, isFetching }] = useLazyGetAllVolumesQuery();

const getVolumesRequest = (params?: TVolumesListRequestParams) => {
lastRequestParams.current = params;

return getVolumes({
project_name,
only_active,
Expand All @@ -116,6 +121,13 @@ export const useVolumesData = ({ project_name, only_active }: TVolumesListReques
});
};

const refreshList = () => {
getVolumesRequest(lastRequestParams.current).then((result) => {
setDisabledNext(false);
setData(result);
});
};

useEffect(() => {
getVolumesRequest().then((result) => {
setPagesCount(1);
Expand Down Expand Up @@ -171,7 +183,7 @@ export const useVolumesData = ({ project_name, only_active }: TVolumesListReques
}
};

return { data, pagesCount, disabledNext, isLoading: isLoading || isFetching, nextPage, prevPage };
return { data, pagesCount, disabledNext, isLoading: isLoading || isFetching, nextPage, prevPage, refreshList };
};

export const useFilters = (storagePrefix?: string) => {
Expand Down Expand Up @@ -211,3 +223,48 @@ export const useFilters = (storagePrefix?: string) => {
isDisabledClearFilter,
} as const;
};

export const useVolumesDelete = () => {
const { t } = useTranslation();
const [deleteVolumesRequest] = useDeleteVolumesMutation();
const [pushNotification] = useNotifications();
const [isDeleting, setIsDeleting] = useState(() => false);

const namesOfVolumesGroupByProjectName = (volumes: IVolume[]) => {
return volumes.reduce<Record<string, string[]>>((acc, volume) => {
if (acc[volume.project_name]) {
acc[volume.project_name].push(volume.name);
} else {
acc[volume.project_name] = [volume.name];
}

return acc;
}, {});
};

const deleteVolumes = (volumes: IVolume[]) => {
if (!volumes.length) return Promise.reject('No volumes');

setIsDeleting(true);

const groupedVolumes = namesOfVolumesGroupByProjectName(volumes);

const requests = Object.keys(groupedVolumes).map((projectName) => {
return deleteVolumesRequest({
project_name: projectName,
names: groupedVolumes[projectName],
}).unwrap();
});

return Promise.all(requests)
.finally(() => setIsDeleting(false))
.catch((error) => {
pushNotification({
type: 'error',
content: t('common.server_error', { error: error?.error }),
});
});
};

return { isDeleting, deleteVolumes };
};
45 changes: 40 additions & 5 deletions frontend/src/pages/Volumes/List/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React from 'react';
import { useTranslation } from 'react-i18next';

import { Button, FormField, Header, Pagination, SelectCSD, Table, Toggle } from 'components';
import { Button, ButtonWithConfirmation, FormField, Header, Pagination, SelectCSD, Table, Toggle } from 'components';

import { useBreadcrumbs, useCollection } from 'hooks';
import { ROUTES } from 'routes';

import { useColumnsDefinitions, useFilters, useVolumesData, useVolumesTableEmptyMessages } from './hooks';
import { useColumnsDefinitions, useFilters, useVolumesData, useVolumesDelete, useVolumesTableEmptyMessages } from './hooks';

import styles from './styles.module.scss';

Expand All @@ -22,12 +22,14 @@ export const VolumeList: React.FC = () => {
setSelectedProject,
} = useFilters();

const { isDeleting, deleteVolumes } = useVolumesDelete();

const { renderEmptyMessage, renderNoMatchMessage } = useVolumesTableEmptyMessages({
clearFilters,
isDisabledClearFilter,
});

const { data, isLoading, pagesCount, disabledNext, prevPage, nextPage } = useVolumesData({
const { data, isLoading, pagesCount, disabledNext, prevPage, nextPage, refreshList } = useVolumesData({
project_name: selectedProject?.value ?? undefined,
only_active: onlyActive,
});
Expand All @@ -41,7 +43,7 @@ export const VolumeList: React.FC = () => {
},
]);

const { items, collectionProps } = useCollection(data, {
const { items, collectionProps, actions } = useCollection(data, {
filtering: {
empty: renderEmptyMessage(),
noMatch: renderNoMatchMessage(),
Expand All @@ -50,6 +52,21 @@ export const VolumeList: React.FC = () => {
selection: {},
});

const { selectedItems } = collectionProps;

const deleteSelectedVolumes = () => {
if (!selectedItems?.length) return;

deleteVolumes([...selectedItems])
.finally(() => {
refreshList();
actions.setSelectedItems([]);
})
.catch(console.log);
};

const isDisabledDeleteSelected = !selectedItems?.length || isDeleting;

return (
<Table
{...collectionProps}
Expand All @@ -59,7 +76,25 @@ export const VolumeList: React.FC = () => {
loading={isLoading}
loadingText={t('common.loading')}
stickyHeader={true}
header={<Header>{t('volume.volumes')}</Header>}
header={
<Header
variant="awsui-h1-sticky"
actions={
<ButtonWithConfirmation
disabled={isDisabledDeleteSelected}
formAction="none"
onClick={deleteSelectedVolumes}
confirmTitle={t('volume.delete_volumes_confirm_title')}
confirmContent={t('volume.delete_volumes_confirm_message')}
>
{t('common.delete')}
</ButtonWithConfirmation>
}
>
{t('volume.volumes')}
</Header>
}
selectionType="multi"
filter={
<div className={styles.filters}>
<div className={styles.select}>
Expand Down
14 changes: 13 additions & 1 deletion frontend/src/services/volume.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,19 @@ export const volumeApi = createApi({
providesTags: (result) =>
result ? [...result.map(({ id }) => ({ type: 'Volumes' as const, id: id })), 'Volumes'] : ['Volumes'],
}),

deleteVolumes: builder.mutation<void, { project_name: IProject['project_name']; names: IVolume['name'][] }>({
query: ({ project_name, names }) => ({
url: API.PROJECTS.VOLUMES_DELETE(project_name),
method: 'POST',
body: {
names,
},
}),

invalidatesTags: () => ['Volumes'],
}),
}),
});

export const { useLazyGetAllVolumesQuery } = volumeApi;
export const { useLazyGetAllVolumesQuery, useDeleteVolumesMutation } = volumeApi;

0 comments on commit b236b8e

Please sign in to comment.