mirror of
https://github.com/portainer/portainer.git
synced 2025-08-08 23:35:31 +02:00
refactor(tables): use add and delete buttons [EE-6297] (#10668)
Co-authored-by: Chaim Lev-Ari <chaim.levi-ari@portaienr.io>
This commit is contained in:
parent
d88ef03ddb
commit
9600eb6fa1
41 changed files with 369 additions and 727 deletions
|
@ -1,10 +1,9 @@
|
|||
import { useRouter } from '@uirouter/react';
|
||||
import { Trash2 } from 'lucide-react';
|
||||
|
||||
import { pluralize } from '@/portainer/helpers/strings';
|
||||
|
||||
import { confirmDestructive } from '@@/modals/confirm';
|
||||
import { AddButton, Button } from '@@/buttons';
|
||||
import { AddButton } from '@@/buttons';
|
||||
import { DeleteButton } from '@@/buttons/DeleteButton';
|
||||
|
||||
import { HelmRepository } from './types';
|
||||
import { useDeleteHelmRepositoriesMutation } from './helm-repositories.service';
|
||||
|
@ -18,37 +17,27 @@ export function HelmRepositoryDatatableActions({ selectedItems }: Props) {
|
|||
const deleteHelmRepoMutation = useDeleteHelmRepositoriesMutation();
|
||||
|
||||
return (
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
disabled={selectedItems.length < 1}
|
||||
color="dangerlight"
|
||||
onClick={() => onDeleteClick(selectedItems)}
|
||||
<>
|
||||
<DeleteButton
|
||||
disabled={selectedItems.length === 0}
|
||||
onConfirmed={() => onDeleteClick(selectedItems)}
|
||||
confirmMessage={`Are you sure you want to remove the selected Helm ${pluralize(
|
||||
selectedItems.length,
|
||||
'repository',
|
||||
'repositories'
|
||||
)}?`}
|
||||
data-cy="credentials-deleteButton"
|
||||
icon={Trash2}
|
||||
/>
|
||||
<AddButton
|
||||
to="portainer.account.createHelmRepository"
|
||||
data-cy="credentials-addButton"
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
|
||||
<AddButton to="portainer.account.createHelmRepository">
|
||||
Add Helm repository
|
||||
</AddButton>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
async function onDeleteClick(selectedItems: HelmRepository[]) {
|
||||
const confirmed = await confirmDestructive({
|
||||
title: 'Confirm action',
|
||||
message: `Are you sure you want to remove the selected Helm ${pluralize(
|
||||
selectedItems.length,
|
||||
'repository',
|
||||
'repositories'
|
||||
)}?`,
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
deleteHelmRepoMutation.mutate(selectedItems, {
|
||||
onSuccess: () => {
|
||||
router.stateService.reload();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { HardDrive, Plus, Trash2 } from 'lucide-react';
|
||||
import { HardDrive, Trash2 } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { useEnvironmentList } from '@/react/portainer/environments/queries';
|
||||
|
@ -6,8 +6,7 @@ import { useGroups } from '@/react/portainer/environments/environment-groups/que
|
|||
|
||||
import { Datatable } from '@@/datatables';
|
||||
import { createPersistedStore } from '@@/datatables/types';
|
||||
import { Button } from '@@/buttons';
|
||||
import { Link } from '@@/Link';
|
||||
import { AddButton, Button } from '@@/buttons';
|
||||
import { useTableState } from '@@/datatables/useTableState';
|
||||
|
||||
import { isBE } from '../../feature-flags/feature-flags.service';
|
||||
|
@ -86,26 +85,20 @@ export function EnvironmentsDatatable({
|
|||
<ImportFdoDeviceButton />
|
||||
|
||||
{isBE && (
|
||||
<Button
|
||||
as={Link}
|
||||
<AddButton
|
||||
color="secondary"
|
||||
icon={Plus}
|
||||
props={{ to: 'portainer.endpoints.edgeAutoCreateScript' }}
|
||||
to="portainer.endpoints.edgeAutoCreateScript"
|
||||
>
|
||||
Auto onboarding
|
||||
</Button>
|
||||
</AddButton>
|
||||
)}
|
||||
<Link to="portainer.wizard.endpoints">
|
||||
<Button
|
||||
onClick={() =>
|
||||
localStorage.setItem('wizardReferrer', 'environments')
|
||||
}
|
||||
icon={Plus}
|
||||
className="!m-0"
|
||||
>
|
||||
Add environment
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
<AddButton
|
||||
to="portainer.wizard.endpoints"
|
||||
params={{ referrer: 'environments' }}
|
||||
>
|
||||
Add environment
|
||||
</AddButton>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
import { Plus } from 'lucide-react';
|
||||
|
||||
import { Button } from '@@/buttons';
|
||||
import { Link } from '@@/Link';
|
||||
import { AddButton } from '@@/buttons';
|
||||
|
||||
import { useSettings } from '../../settings/queries';
|
||||
import {
|
||||
|
@ -22,15 +19,10 @@ export function ImportFdoDeviceButton() {
|
|||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
type="button"
|
||||
color="secondary"
|
||||
icon={Plus}
|
||||
as={Link}
|
||||
props={{ to: 'portainer.endpoints.importDevice' }}
|
||||
className="ml-[5px]"
|
||||
>
|
||||
Import FDO device
|
||||
</Button>
|
||||
<div className="ml-[5px]">
|
||||
<AddButton color="secondary" to="portainer.endpoints.importDevice">
|
||||
Import FDO device
|
||||
</AddButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Clock, Trash2 } from 'lucide-react';
|
||||
import { Clock } from 'lucide-react';
|
||||
import { useMemo } from 'react';
|
||||
import _ from 'lodash';
|
||||
|
||||
|
@ -6,12 +6,11 @@ import { notifySuccess } from '@/portainer/services/notifications';
|
|||
import { withLimitToBE } from '@/react/hooks/useLimitToBE';
|
||||
import { useEdgeGroups } from '@/react/edge/edge-groups/queries/useEdgeGroups';
|
||||
|
||||
import { confirmDelete } from '@@/modals/confirm';
|
||||
import { Datatable } from '@@/datatables';
|
||||
import { PageHeader } from '@@/PageHeader';
|
||||
import { Button } from '@@/buttons';
|
||||
import { Link } from '@@/Link';
|
||||
import { AddButton } from '@@/buttons';
|
||||
import { useTableState } from '@@/datatables/useTableState';
|
||||
import { DeleteButton } from '@@/buttons/DeleteButton';
|
||||
|
||||
import { useList } from '../queries/list';
|
||||
import { EdgeUpdateSchedule, StatusType } from '../types';
|
||||
|
@ -90,29 +89,16 @@ function TableActions({
|
|||
const removeMutation = useRemoveMutation();
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
icon={Trash2}
|
||||
color="dangerlight"
|
||||
onClick={() => handleRemove()}
|
||||
<DeleteButton
|
||||
onConfirmed={() => handleRemove()}
|
||||
disabled={selectedRows.length === 0}
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
|
||||
<Link to=".create">
|
||||
<Button>Add update & rollback schedule</Button>
|
||||
</Link>
|
||||
confirmMessage="Are you sure you want to remove these schedules?"
|
||||
/>
|
||||
<AddButton to=".create">Add update & rollback schedule</AddButton>
|
||||
</>
|
||||
);
|
||||
|
||||
async function handleRemove() {
|
||||
const confirmed = await confirmDelete(
|
||||
'Are you sure you want to remove these?'
|
||||
);
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
removeMutation.mutate(selectedRows, {
|
||||
onSuccess: () => {
|
||||
notifySuccess('Success', 'Schedules successfully removed');
|
||||
|
|
|
@ -33,7 +33,7 @@ import { WizardEndpointsList } from './WizardEndpointsList';
|
|||
|
||||
export function EnvironmentCreationView() {
|
||||
const {
|
||||
params: { localEndpointId: localEndpointIdParam },
|
||||
params: { localEndpointId: localEndpointIdParam, referrer },
|
||||
} = useCurrentStateAndParams();
|
||||
|
||||
const [environmentIds, setEnvironmentIds] = useState<EnvironmentId[]>(() => {
|
||||
|
@ -130,8 +130,7 @@ export function EnvironmentCreationView() {
|
|||
])
|
||||
),
|
||||
});
|
||||
if (localStorage.getItem('wizardReferrer') === 'environments') {
|
||||
localStorage.removeItem('wizardReferrer');
|
||||
if (referrer === 'environments') {
|
||||
router.stateService.go('portainer.endpoints');
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Bell, Trash2 } from 'lucide-react';
|
||||
import { Bell } from 'lucide-react';
|
||||
import { useStore } from 'zustand';
|
||||
import { useCurrentStateAndParams } from '@uirouter/react';
|
||||
|
||||
|
@ -10,9 +10,9 @@ import { withReactQuery } from '@/react-tools/withReactQuery';
|
|||
|
||||
import { PageHeader } from '@@/PageHeader';
|
||||
import { Datatable } from '@@/datatables';
|
||||
import { Button } from '@@/buttons';
|
||||
import { createPersistedStore } from '@@/datatables/types';
|
||||
import { useTableState } from '@@/datatables/useTableState';
|
||||
import { DeleteButton } from '@@/buttons/DeleteButton';
|
||||
|
||||
import { notificationsStore } from './notifications-store';
|
||||
import { ToastNotification } from './types';
|
||||
|
@ -62,14 +62,11 @@ function TableActions({ selectedRows }: { selectedRows: ToastNotification[] }) {
|
|||
const { user } = useUser();
|
||||
const notificationsStoreState = useStore(notificationsStore);
|
||||
return (
|
||||
<Button
|
||||
icon={Trash2}
|
||||
color="dangerlight"
|
||||
onClick={() => handleRemove()}
|
||||
<DeleteButton
|
||||
onConfirmed={() => handleRemove()}
|
||||
disabled={selectedRows.length === 0}
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
confirmMessage="Are you sure you want to remove the selected notifications?"
|
||||
/>
|
||||
);
|
||||
|
||||
function handleRemove() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { useQueryClient } from 'react-query';
|
||||
import { useRouter } from '@uirouter/react';
|
||||
import { PlusCircle, Trash2 } from 'lucide-react';
|
||||
import { PlusCircle } from 'lucide-react';
|
||||
|
||||
import { Profile } from '@/portainer/hostmanagement/fdo/model';
|
||||
import * as notifications from '@/portainer/services/notifications';
|
||||
|
@ -9,10 +9,10 @@ import {
|
|||
duplicateProfile,
|
||||
} from '@/portainer/hostmanagement/fdo/fdo.service';
|
||||
|
||||
import { confirm, confirmDestructive } from '@@/modals/confirm';
|
||||
import { confirm } from '@@/modals/confirm';
|
||||
import { Link } from '@@/Link';
|
||||
import { Button } from '@@/buttons';
|
||||
import { buildConfirmButton } from '@@/modals/utils';
|
||||
import { DeleteButton } from '@@/buttons/DeleteButton';
|
||||
|
||||
interface Props {
|
||||
isFDOEnabled: boolean;
|
||||
|
@ -27,7 +27,7 @@ export function FDOProfilesDatatableActions({
|
|||
const queryClient = useQueryClient();
|
||||
|
||||
return (
|
||||
<div className="actionBar">
|
||||
<>
|
||||
<Link to="portainer.endpoints.profile" className="space-left">
|
||||
<Button disabled={!isFDOEnabled} icon={PlusCircle}>
|
||||
Add Profile
|
||||
|
@ -42,15 +42,12 @@ export function FDOProfilesDatatableActions({
|
|||
Duplicate
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
disabled={!isFDOEnabled || selectedItems.length < 1}
|
||||
color="danger"
|
||||
onClick={() => onDeleteProfileClick()}
|
||||
icon={Trash2}
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
</div>
|
||||
<DeleteButton
|
||||
disabled={!isFDOEnabled || selectedItems.length === 0}
|
||||
onConfirmed={() => onDeleteProfileClick()}
|
||||
confirmMessage="This action will delete the selected profile(s). Continue?"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
async function onDuplicateProfileClick() {
|
||||
|
@ -80,16 +77,6 @@ export function FDOProfilesDatatableActions({
|
|||
}
|
||||
|
||||
async function onDeleteProfileClick() {
|
||||
const confirmed = await confirmDestructive({
|
||||
title: 'Are you sure?',
|
||||
message: 'This action will delete the selected profile(s). Continue?',
|
||||
confirmButton: buildConfirmButton('Remove', 'danger'),
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
selectedItems.map(async (profile) => {
|
||||
try {
|
||||
|
|
|
@ -2,7 +2,9 @@ import userEvent from '@testing-library/user-event';
|
|||
import { PropsWithChildren } from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import { AppTemplatesListItem } from './AppTemplatesListItem';
|
||||
import { withTestRouter } from '@/react/test-utils/withRouter';
|
||||
|
||||
import { AppTemplatesListItem as BaseComponent } from './AppTemplatesListItem';
|
||||
import { TemplateViewModel } from './view-model';
|
||||
import { TemplateType } from './types';
|
||||
|
||||
|
@ -15,13 +17,7 @@ test('should render AppTemplatesListItem component', () => {
|
|||
const onSelect = vi.fn();
|
||||
const isSelected = false;
|
||||
|
||||
const { getByText } = render(
|
||||
<AppTemplatesListItem
|
||||
template={template}
|
||||
onSelect={onSelect}
|
||||
isSelected={isSelected}
|
||||
/>
|
||||
);
|
||||
const { getByText } = renderComponent({ isSelected, template, onSelect });
|
||||
|
||||
expect(getByText(template.Title, { exact: false })).toBeInTheDocument();
|
||||
});
|
||||
|
@ -45,26 +41,23 @@ const copyAsCustomTestCases = [
|
|||
vi.mock('@uirouter/react', async (importOriginal: () => Promise<object>) => ({
|
||||
...(await importOriginal()),
|
||||
UISref: ({ children }: PropsWithChildren<unknown>) => children, // Mocking UISref to render its children directly
|
||||
useSref: () => ({ href: '' }), // Mocking useSref to return an empty string
|
||||
}));
|
||||
|
||||
copyAsCustomTestCases.forEach(({ type, expected }) => {
|
||||
test(`copy as custom button should ${
|
||||
expected ? '' : 'not '
|
||||
}be rendered for type ${type}`, () => {
|
||||
}be rendered for type ${TemplateType[type]}`, () => {
|
||||
const onSelect = vi.fn();
|
||||
const isSelected = false;
|
||||
|
||||
const { queryByText, unmount } = render(
|
||||
<AppTemplatesListItem
|
||||
template={
|
||||
{
|
||||
Type: type,
|
||||
} as TemplateViewModel
|
||||
}
|
||||
onSelect={onSelect}
|
||||
isSelected={isSelected}
|
||||
/>
|
||||
);
|
||||
const { queryByText, unmount } = renderComponent({
|
||||
isSelected,
|
||||
template: {
|
||||
Type: type,
|
||||
} as TemplateViewModel,
|
||||
onSelect,
|
||||
});
|
||||
|
||||
if (expected) {
|
||||
expect(queryByText('Copy as Custom')).toBeVisible();
|
||||
|
@ -86,16 +79,34 @@ test('should call onSelect when clicked', async () => {
|
|||
const onSelect = vi.fn();
|
||||
const isSelected = false;
|
||||
|
||||
const { getByLabelText } = render(
|
||||
<AppTemplatesListItem
|
||||
template={template}
|
||||
onSelect={onSelect}
|
||||
isSelected={isSelected}
|
||||
/>
|
||||
);
|
||||
const { getByLabelText } = renderComponent({
|
||||
isSelected,
|
||||
template,
|
||||
onSelect,
|
||||
});
|
||||
|
||||
const button = getByLabelText(template.Title);
|
||||
await user.click(button);
|
||||
|
||||
expect(onSelect).toHaveBeenCalledWith(template);
|
||||
});
|
||||
|
||||
function renderComponent({
|
||||
isSelected = false,
|
||||
onSelect,
|
||||
template,
|
||||
}: {
|
||||
template: TemplateViewModel;
|
||||
onSelect?: () => void;
|
||||
isSelected?: boolean;
|
||||
}) {
|
||||
const AppTemplatesListItem = withTestRouter(BaseComponent);
|
||||
|
||||
return render(
|
||||
<AppTemplatesListItem
|
||||
template={template}
|
||||
onSelect={onSelect}
|
||||
isSelected={isSelected}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Edit, Plus } from 'lucide-react';
|
||||
import { Edit } from 'lucide-react';
|
||||
import _ from 'lodash';
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
|
@ -9,8 +9,7 @@ import { Table } from '@@/datatables';
|
|||
import { useTableState } from '@@/datatables/useTableState';
|
||||
import { createPersistedStore } from '@@/datatables/types';
|
||||
import { DatatableFooter } from '@@/datatables/DatatableFooter';
|
||||
import { Button } from '@@/buttons';
|
||||
import { Link } from '@@/Link';
|
||||
import { AddButton } from '@@/buttons';
|
||||
|
||||
import { CustomTemplatesListItem } from './CustomTemplatesListItem';
|
||||
|
||||
|
@ -56,11 +55,7 @@ export function CustomTemplatesList({
|
|||
searchValue={listState.search}
|
||||
title="Custom Templates"
|
||||
titleIcon={Edit}
|
||||
renderTableActions={() => (
|
||||
<Button as={Link} props={{ to: '.new' }} icon={Plus}>
|
||||
Add Custom Template
|
||||
</Button>
|
||||
)}
|
||||
renderTableActions={() => <AddButton>Add Custom Template</AddButton>}
|
||||
/>
|
||||
|
||||
<div className="blocklist gap-y-2 !px-[20px] !pb-[20px]" role="list">
|
||||
|
|
|
@ -46,7 +46,6 @@ export function CustomTemplatesListItem({
|
|||
<Button
|
||||
as={Link}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
color="secondary"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { useRouter } from '@uirouter/react';
|
||||
import { useMutation, useQueryClient } from 'react-query';
|
||||
import { Trash2, Users } from 'lucide-react';
|
||||
import { Users } from 'lucide-react';
|
||||
|
||||
import { usePublicSettings } from '@/react/portainer/settings/queries';
|
||||
import {
|
||||
|
@ -9,9 +9,8 @@ import {
|
|||
withInvalidate,
|
||||
} from '@/react-tools/react-query';
|
||||
|
||||
import { confirmDelete } from '@@/modals/confirm';
|
||||
import { Button } from '@@/buttons';
|
||||
import { Widget } from '@@/Widget';
|
||||
import { DeleteButton } from '@@/buttons/DeleteButton';
|
||||
|
||||
import { Team, TeamId, TeamMembership, TeamRole } from '../types';
|
||||
import { deleteTeam } from '../teams.service';
|
||||
|
@ -45,17 +44,18 @@ export function Details({ team, memberships, isAdmin }: Props) {
|
|||
<tr>
|
||||
<td>Name</td>
|
||||
<td>
|
||||
{!teamSyncQuery.data && team.Name}
|
||||
{isAdmin && (
|
||||
<Button
|
||||
color="danger"
|
||||
size="xsmall"
|
||||
onClick={handleDeleteClick}
|
||||
icon={Trash2}
|
||||
>
|
||||
Delete this team
|
||||
</Button>
|
||||
)}
|
||||
<div className="flex gap-2">
|
||||
{!teamSyncQuery.data && team.Name}
|
||||
{isAdmin && (
|
||||
<DeleteButton
|
||||
size="xsmall"
|
||||
onConfirmed={handleDeleteClick}
|
||||
confirmMessage="Do you want to delete this team? Users in this team will not be deleted."
|
||||
>
|
||||
Delete this team
|
||||
</DeleteButton>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -75,18 +75,8 @@ export function Details({ team, memberships, isAdmin }: Props) {
|
|||
);
|
||||
|
||||
async function handleDeleteClick() {
|
||||
const confirmed = await confirmDelete(
|
||||
`Do you want to delete this team? Users in this team will not be deleted.`
|
||||
);
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
deleteMutation.mutate(team.Id, {
|
||||
onSuccess() {
|
||||
router.stateService.go('portainer.teams');
|
||||
},
|
||||
});
|
||||
router.stateService.go('portainer.teams');
|
||||
deleteMutation.mutate(team.Id);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { useMutation, useQueryClient } from 'react-query';
|
||||
import { Trash2, Users } from 'lucide-react';
|
||||
import { Users } from 'lucide-react';
|
||||
import { ColumnDef } from '@tanstack/react-table';
|
||||
|
||||
import { notifySuccess } from '@/portainer/services/notifications';
|
||||
|
@ -7,12 +7,11 @@ import { promiseSequence } from '@/portainer/helpers/promise-utils';
|
|||
import { Team, TeamId } from '@/react/portainer/users/teams/types';
|
||||
import { deleteTeam } from '@/react/portainer/users/teams/teams.service';
|
||||
|
||||
import { confirmDelete } from '@@/modals/confirm';
|
||||
import { Datatable } from '@@/datatables';
|
||||
import { Button } from '@@/buttons';
|
||||
import { buildNameColumn } from '@@/datatables/buildNameColumn';
|
||||
import { createPersistedStore } from '@@/datatables/types';
|
||||
import { useTableState } from '@@/datatables/useTableState';
|
||||
import { DeleteButton } from '@@/buttons/DeleteButton';
|
||||
|
||||
const storageKey = 'teams';
|
||||
|
||||
|
@ -40,14 +39,11 @@ export function TeamsDatatable({ teams, isAdmin }: Props) {
|
|||
titleIcon={Users}
|
||||
renderTableActions={(selectedRows) =>
|
||||
isAdmin && (
|
||||
<Button
|
||||
color="dangerlight"
|
||||
onClick={() => handleRemoveClick(selectedRows)}
|
||||
<DeleteButton
|
||||
onConfirmed={() => handleRemoveClick(selectedRows)}
|
||||
disabled={selectedRows.length === 0}
|
||||
icon={Trash2}
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
confirmMessage="Are you sure you want to remove the selected teams?"
|
||||
/>
|
||||
)
|
||||
}
|
||||
emptyContentLabel="No teams found"
|
||||
|
@ -79,14 +75,6 @@ function useRemoveMutation() {
|
|||
return { handleRemove };
|
||||
|
||||
async function handleRemove(teams: TeamId[]) {
|
||||
const confirmed = await confirmDelete(
|
||||
'Are you sure you want to remove the selected teams?'
|
||||
);
|
||||
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
deleteMutation.mutate(teams, {
|
||||
onSuccess: () => {
|
||||
notifySuccess('Teams successfully removed', '');
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue