diff --git a/app/docker/views/configs/configsController.js b/app/docker/views/configs/configsController.js
index 0bf7169b0..2d6168172 100644
--- a/app/docker/views/configs/configsController.js
+++ b/app/docker/views/configs/configsController.js
@@ -1,5 +1,4 @@
import angular from 'angular';
-import { confirmDelete } from '@@/modals/confirm';
class ConfigsController {
/* @ngInject */
@@ -34,10 +33,6 @@ class ConfigsController {
}
async removeAction(selectedItems) {
- const confirmed = await confirmDelete('Do you want to remove the selected config(s)?');
- if (!confirmed) {
- return null;
- }
return this.$async(this.removeActionAsync, selectedItems);
}
diff --git a/app/docker/views/networks/networksController.js b/app/docker/views/networks/networksController.js
index 87d4b3797..61249bcde 100644
--- a/app/docker/views/networks/networksController.js
+++ b/app/docker/views/networks/networksController.js
@@ -1,6 +1,5 @@
import _ from 'lodash-es';
import DockerNetworkHelper from '@/docker/helpers/networkHelper';
-import { confirmDelete } from '@@/modals/confirm';
angular.module('portainer.docker').controller('NetworksController', [
'$q',
@@ -13,10 +12,6 @@ angular.module('portainer.docker').controller('NetworksController', [
'AgentService',
function ($q, $scope, $state, NetworkService, Notifications, HttpRequestHelper, endpoint, AgentService) {
$scope.removeAction = async function (selectedItems) {
- const confirmed = await confirmDelete('Do you want to remove the selected network(s)?');
- if (!confirmed) {
- return null;
- }
var actionCount = selectedItems.length;
angular.forEach(selectedItems, function (network) {
HttpRequestHelper.setPortainerAgentTargetHeader(network.NodeName);
diff --git a/app/docker/views/secrets/secretsController.js b/app/docker/views/secrets/secretsController.js
index d59a69472..6b7cb68ca 100644
--- a/app/docker/views/secrets/secretsController.js
+++ b/app/docker/views/secrets/secretsController.js
@@ -1,4 +1,3 @@
-import { confirmDelete } from '@@/modals/confirm';
angular.module('portainer.docker').controller('SecretsController', [
'$scope',
'$state',
@@ -6,10 +5,6 @@ angular.module('portainer.docker').controller('SecretsController', [
'Notifications',
function ($scope, $state, SecretService, Notifications) {
$scope.removeAction = async function (selectedItems) {
- const confirmed = await confirmDelete('Do you want to remove the selected secret(s)?');
- if (!confirmed) {
- return null;
- }
var actionCount = selectedItems.length;
angular.forEach(selectedItems, function (secret) {
SecretService.remove(secret.Id)
diff --git a/app/docker/views/volumes/volumesController.js b/app/docker/views/volumes/volumesController.js
index 0ca1352d6..7f9740770 100644
--- a/app/docker/views/volumes/volumesController.js
+++ b/app/docker/views/volumes/volumesController.js
@@ -1,5 +1,3 @@
-import { confirmDelete } from '@@/modals/confirm';
-
angular.module('portainer.docker').controller('VolumesController', [
'$q',
'$scope',
@@ -13,28 +11,24 @@ angular.module('portainer.docker').controller('VolumesController', [
'endpoint',
function ($q, $scope, $state, VolumeService, ServiceService, VolumeHelper, Notifications, HttpRequestHelper, Authentication, endpoint) {
$scope.removeAction = function (selectedItems) {
- confirmDelete('Do you want to remove the selected volume(s)?').then((confirmed) => {
- if (confirmed) {
- var actionCount = selectedItems.length;
- angular.forEach(selectedItems, function (volume) {
- HttpRequestHelper.setPortainerAgentTargetHeader(volume.NodeName);
- VolumeService.remove(volume)
- .then(function success() {
- Notifications.success('Volume successfully removed', volume.Id);
- var index = $scope.volumes.indexOf(volume);
- $scope.volumes.splice(index, 1);
- })
- .catch(function error(err) {
- Notifications.error('Failure', err, 'Unable to remove volume');
- })
- .finally(function final() {
- --actionCount;
- if (actionCount === 0) {
- $state.reload();
- }
- });
+ var actionCount = selectedItems.length;
+ angular.forEach(selectedItems, function (volume) {
+ HttpRequestHelper.setPortainerAgentTargetHeader(volume.NodeName);
+ VolumeService.remove(volume)
+ .then(function success() {
+ Notifications.success('Volume successfully removed', volume.Id);
+ var index = $scope.volumes.indexOf(volume);
+ $scope.volumes.splice(index, 1);
+ })
+ .catch(function error(err) {
+ Notifications.error('Failure', err, 'Unable to remove volume');
+ })
+ .finally(function final() {
+ --actionCount;
+ if (actionCount === 0) {
+ $state.reload();
+ }
});
- }
});
};
diff --git a/app/kubernetes/views/applications/applicationsController.js b/app/kubernetes/views/applications/applicationsController.js
index 5ac361176..f125cac8b 100644
--- a/app/kubernetes/views/applications/applicationsController.js
+++ b/app/kubernetes/views/applications/applicationsController.js
@@ -79,11 +79,7 @@ class KubernetesApplicationsController {
}
removeStacksAction(selectedItems) {
- confirmDelete('Are you sure that you want to remove the selected stack(s) ? This will remove all the applications associated to the stack(s).').then((confirmed) => {
- if (confirmed) {
- return this.$async(this.removeStacksActionAsync, selectedItems);
- }
- });
+ return this.$async(this.removeStacksActionAsync, selectedItems);
}
async removeActionAsync(selectedItems) {
diff --git a/app/portainer/react/views/wizard.ts b/app/portainer/react/views/wizard.ts
index 38e327fa4..65b95495e 100644
--- a/app/portainer/react/views/wizard.ts
+++ b/app/portainer/react/views/wizard.ts
@@ -50,7 +50,7 @@ function config($stateRegistryProvider: StateRegistry) {
$stateRegistryProvider.register({
name: 'portainer.wizard.endpoints',
- url: '/endpoints',
+ url: '/endpoints?referrer',
views: {
'content@': {
component: 'wizardEnvironmentTypeSelectView',
diff --git a/app/portainer/views/stacks/stacksController.js b/app/portainer/views/stacks/stacksController.js
index 1d7543d55..1a9e2d454 100644
--- a/app/portainer/views/stacks/stacksController.js
+++ b/app/portainer/views/stacks/stacksController.js
@@ -1,16 +1,9 @@
-import { confirmDelete } from '@@/modals/confirm';
-
angular.module('portainer.app').controller('StacksController', StacksController);
/* @ngInject */
function StacksController($scope, $state, Notifications, StackService, Authentication, endpoint) {
$scope.removeAction = function (selectedItems) {
- confirmDelete('Do you want to remove the selected stack(s)? Associated services will be removed as well.').then((confirmed) => {
- if (!confirmed) {
- return;
- }
- deleteSelectedStacks(selectedItems);
- });
+ return deleteSelectedStacks(selectedItems);
};
function deleteSelectedStacks(stacks) {
diff --git a/app/react/azure/container-instances/ListView/ContainersDatatable.tsx b/app/react/azure/container-instances/ListView/ContainersDatatable.tsx
index 3d7f4e872..f207d288b 100644
--- a/app/react/azure/container-instances/ListView/ContainersDatatable.tsx
+++ b/app/react/azure/container-instances/ListView/ContainersDatatable.tsx
@@ -1,14 +1,13 @@
-import { Box, Plus, Trash2 } from 'lucide-react';
+import { Box } from 'lucide-react';
import { ContainerGroup } from '@/react/azure/types';
import { Authorized } from '@/react/hooks/useUser';
-import { confirmDelete } from '@@/modals/confirm';
import { Datatable } from '@@/datatables';
-import { Button } from '@@/buttons';
-import { Link } from '@@/Link';
+import { AddButton } from '@@/buttons';
import { createPersistedStore } from '@@/datatables/types';
import { useTableState } from '@@/datatables/useTableState';
+import { DeleteButton } from '@@/buttons/DeleteButton';
import { columns } from './columns';
@@ -33,36 +32,26 @@ export function ContainersDatatable({ dataset, onRemoveClick }: Props) {
getRowId={(container) => container.id}
emptyContentLabel="No container available."
renderTableActions={(selectedRows) => (
- <>
+
-
+ onConfirmed={() =>
+ handleRemoveClick(selectedRows.map((r) => r.id))
+ }
+ confirmMessage="Are you sure you want to delete the selected containers?"
+ />
-
-
-
+ Add container
- >
+
)}
/>
);
async function handleRemoveClick(containerIds: string[]) {
- const confirmed = await confirmDelete(
- 'Are you sure you want to delete the selected containers?'
- );
- if (!confirmed) {
- return null;
- }
-
return onRemoveClick(containerIds);
}
}
diff --git a/app/react/components/Link.tsx b/app/react/components/Link.tsx
index 90571009f..42f012577 100644
--- a/app/react/components/Link.tsx
+++ b/app/react/components/Link.tsx
@@ -1,5 +1,5 @@
import { PropsWithChildren, AnchorHTMLAttributes } from 'react';
-import { UISref, UISrefProps } from '@uirouter/react';
+import { UISrefProps, useSref } from '@uirouter/react';
interface Props {
title?: string;
@@ -8,18 +8,18 @@ interface Props {
}
export function Link({
- title = '',
- className,
children,
+ to,
+ params,
+ options,
...props
}: PropsWithChildren & UISrefProps) {
+ const { onClick, href } = useSref(to, params, options);
+
return (
// eslint-disable-next-line react/jsx-props-no-spreading
-
- {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
-
- {children}
-
-
+
+ {children}
+
);
}
diff --git a/app/react/components/buttons/DeleteButton.tsx b/app/react/components/buttons/DeleteButton.tsx
index e2bbef3f0..bd8968f6d 100644
--- a/app/react/components/buttons/DeleteButton.tsx
+++ b/app/react/components/buttons/DeleteButton.tsx
@@ -1,6 +1,8 @@
import { Trash2 } from 'lucide-react';
import { ComponentProps, PropsWithChildren, ReactNode } from 'react';
+import { AutomationTestingProps } from '@/types';
+
import { confirmDelete } from '@@/modals/confirm';
import { Button } from './Button';
@@ -21,13 +23,15 @@ type ConfirmOrClick =
export function DeleteButton({
disabled,
size,
+ 'data-cy': dataCy,
children,
...props
}: PropsWithChildren<
- ConfirmOrClick & {
- size?: ComponentProps['size'];
- disabled?: boolean;
- }
+ AutomationTestingProps &
+ ConfirmOrClick & {
+ size?: ComponentProps['size'];
+ disabled?: boolean;
+ }
>) {
return (
diff --git a/app/react/docker/configs/ListView/ConfigsDatatable/ConfigsDatatable.tsx b/app/react/docker/configs/ListView/ConfigsDatatable/ConfigsDatatable.tsx
index 7f87933f3..cccc25405 100644
--- a/app/react/docker/configs/ListView/ConfigsDatatable/ConfigsDatatable.tsx
+++ b/app/react/docker/configs/ListView/ConfigsDatatable/ConfigsDatatable.tsx
@@ -1,13 +1,13 @@
-import { Clipboard, Plus, Trash2 } from 'lucide-react';
+import { Clipboard } from 'lucide-react';
import { Authorized, useAuthorizations } from '@/react/hooks/useUser';
import { Datatable, TableSettingsMenu } from '@@/datatables';
import { TableSettingsMenuAutoRefresh } from '@@/datatables/TableSettingsMenuAutoRefresh';
import { useRepeater } from '@@/datatables/useRepeater';
-import { Button } from '@@/buttons';
-import { Link } from '@@/Link';
+import { AddButton } from '@@/buttons';
import { useTableState } from '@@/datatables/useTableState';
+import { DeleteButton } from '@@/buttons/DeleteButton';
import { DockerConfig } from '../../types';
@@ -17,7 +17,7 @@ import { createStore } from './store';
interface Props {
dataset: Array;
onRemoveClick: (configs: Array) => void;
- onRefresh: () => Promise;
+ onRefresh: () => void;
}
const storageKey = 'docker_configs';
@@ -54,24 +54,15 @@ export function ConfigsDatatable({ dataset, onRefresh, onRemoveClick }: Props) {
hasWriteAccessQuery.authorized && (
-
+ onConfirmed={() => onRemoveClick(selectedRows)}
+ confirmMessage="Do you want to remove the selected config(s)?"
+ />
-
+ Add config
)
diff --git a/app/react/docker/containers/ListView/ContainersDatatable/ContainersDatatableActions.tsx b/app/react/docker/containers/ListView/ContainersDatatable/ContainersDatatableActions.tsx
index 360cc333f..580551073 100644
--- a/app/react/docker/containers/ListView/ContainersDatatable/ContainersDatatableActions.tsx
+++ b/app/react/docker/containers/ListView/ContainersDatatable/ContainersDatatableActions.tsx
@@ -1,13 +1,5 @@
import { useRouter } from '@uirouter/react';
-import {
- Pause,
- Play,
- Plus,
- RefreshCw,
- Slash,
- Square,
- Trash2,
-} from 'lucide-react';
+import { Pause, Play, RefreshCw, Slash, Square, Trash2 } from 'lucide-react';
import * as notifications from '@/portainer/services/notifications';
import { useAuthorizations, Authorized } from '@/react/hooks/useUser';
@@ -29,8 +21,7 @@ import {
} from '@/react/docker/containers/containers.service';
import type { EnvironmentId } from '@/react/portainer/environments/types';
-import { Link } from '@@/Link';
-import { ButtonGroup, Button } from '@@/buttons';
+import { ButtonGroup, Button, AddButton } from '@@/buttons';
type ContainerServiceAction = (
endpointId: EnvironmentId,
@@ -166,11 +157,11 @@ export function ContainersDatatableActions({
{isAddActionVisible && (
-
-
-
-
-
+
)}
);
diff --git a/app/react/docker/images/ListView/ImagesDatatable/ImagesDatatable.tsx b/app/react/docker/images/ListView/ImagesDatatable/ImagesDatatable.tsx
index ee408df3f..16fa0ead0 100644
--- a/app/react/docker/images/ListView/ImagesDatatable/ImagesDatatable.tsx
+++ b/app/react/docker/images/ListView/ImagesDatatable/ImagesDatatable.tsx
@@ -1,11 +1,4 @@
-import {
- ChevronDown,
- Download,
- List,
- Plus,
- Trash2,
- Upload,
-} from 'lucide-react';
+import { ChevronDown, Download, List, Trash2, Upload } from 'lucide-react';
import { Menu, MenuButton, MenuItem, MenuPopover } from '@reach/menu-button';
import { positionRight } from '@reach/popover';
import { useMemo } from 'react';
@@ -21,7 +14,7 @@ import {
RefreshableTableSettings,
} from '@@/datatables/types';
import { useTableState } from '@@/datatables/useTableState';
-import { Button, ButtonGroup, LoadingButton } from '@@/buttons';
+import { AddButton, Button, ButtonGroup, LoadingButton } from '@@/buttons';
import { Link } from '@@/Link';
import { ButtonWithRef } from '@@/buttons/Button';
import { TableSettingsMenuAutoRefresh } from '@@/datatables/TableSettingsMenuAutoRefresh';
@@ -82,14 +75,12 @@ export function ImagesDatatable({
/>
-
+
)}
diff --git a/app/react/docker/networks/ItemView/ItemView.tsx b/app/react/docker/networks/ItemView/ItemView.tsx
index a7c396d14..a50f34251 100644
--- a/app/react/docker/networks/ItemView/ItemView.tsx
+++ b/app/react/docker/networks/ItemView/ItemView.tsx
@@ -8,7 +8,6 @@ import { DockerContainer } from '@/react/docker/containers/types';
import { ResourceControlViewModel } from '@/react/portainer/access-control/models/ResourceControlViewModel';
import { useContainers } from '@/react/docker/containers/queries/containers';
-import { confirmDelete } from '@@/modals/confirm';
import { PageHeader } from '@@/PageHeader';
import { useNetwork, useDeleteNetwork } from '../queries';
@@ -95,19 +94,14 @@ export function ItemView() {
);
async function onRemoveNetworkClicked() {
- const message = 'Do you want to delete the network?';
- const confirmed = await confirmDelete(message);
-
- if (confirmed) {
- deleteNetworkMutation.mutate(
- { environmentId, networkId },
- {
- onSuccess: () => {
- router.stateService.go('docker.networks');
- },
- }
- );
- }
+ deleteNetworkMutation.mutate(
+ { environmentId, networkId },
+ {
+ onSuccess: () => {
+ router.stateService.go('docker.networks');
+ },
+ }
+ );
}
}
diff --git a/app/react/docker/networks/ItemView/NetworkDetailsTable.tsx b/app/react/docker/networks/ItemView/NetworkDetailsTable.tsx
index 38a4ac25a..0143131bd 100644
--- a/app/react/docker/networks/ItemView/NetworkDetailsTable.tsx
+++ b/app/react/docker/networks/ItemView/NetworkDetailsTable.tsx
@@ -1,13 +1,12 @@
import { Fragment } from 'react';
-import { Network, Trash2 } from 'lucide-react';
+import { Network } from 'lucide-react';
import DockerNetworkHelper from '@/docker/helpers/networkHelper';
import { Authorized } from '@/react/hooks/useUser';
import { TableContainer, TableTitle } from '@@/datatables';
import { DetailsTable } from '@@/DetailsTable';
-import { Button } from '@@/buttons';
-import { Icon } from '@@/Icon';
+import { DeleteButton } from '@@/buttons/DeleteButton';
import { isSystemNetwork } from '../network.helper';
import { DockerNetwork, IPConfig } from '../types';
@@ -38,21 +37,18 @@ export function NetworkDetailsTable({
{network.Id}
{allowRemoveNetwork && (
-
-
-
+
+
+
+ Delete this network
+
+
+
)}
{network.Driver}
diff --git a/app/react/docker/networks/ListView/NetworksDatatable.tsx b/app/react/docker/networks/ListView/NetworksDatatable.tsx
index f4ad141d7..9c37e5818 100644
--- a/app/react/docker/networks/ListView/NetworksDatatable.tsx
+++ b/app/react/docker/networks/ListView/NetworksDatatable.tsx
@@ -1,4 +1,4 @@
-import { Plus, Network, Trash2 } from 'lucide-react';
+import { Network } from 'lucide-react';
import { Authorized } from '@/react/hooks/useUser';
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
@@ -10,12 +10,12 @@ import {
refreshableSettings,
RefreshableTableSettings,
} from '@@/datatables/types';
-import { Button } from '@@/buttons';
+import { AddButton } from '@@/buttons';
import { TableSettingsMenu } from '@@/datatables';
import { TableSettingsMenuAutoRefresh } from '@@/datatables/TableSettingsMenuAutoRefresh';
import { useRepeater } from '@@/datatables/useRepeater';
import { useTableState } from '@@/datatables/useTableState';
-import { Link } from '@@/Link';
+import { DeleteButton } from '@@/buttons/DeleteButton';
import { useIsSwarm } from '../../proxy/queries/useInfo';
@@ -80,22 +80,16 @@ export function NetworksDatatable({ dataset, onRemove, onRefresh }: Props) {
-
+ confirmMessage="Do you want to remove the selected network(s)?"
+ onConfirmed={() => onRemove(selectedRows)}
+ />
-
-
+
)}
diff --git a/app/react/docker/secrets/ListView/SecretsDatatable.tsx b/app/react/docker/secrets/ListView/SecretsDatatable.tsx
index 81343a4e1..cc53ddac1 100644
--- a/app/react/docker/secrets/ListView/SecretsDatatable.tsx
+++ b/app/react/docker/secrets/ListView/SecretsDatatable.tsx
@@ -1,5 +1,5 @@
import { createColumnHelper } from '@tanstack/react-table';
-import { Lock, Plus, Trash2 } from 'lucide-react';
+import { Lock } from 'lucide-react';
import { SecretViewModel } from '@/docker/models/secret';
import { isoDate } from '@/portainer/filters/filters';
@@ -15,9 +15,9 @@ import {
} from '@@/datatables/types';
import { useTableState } from '@@/datatables/useTableState';
import { TableSettingsMenuAutoRefresh } from '@@/datatables/TableSettingsMenuAutoRefresh';
-import { Button } from '@@/buttons';
-import { Link } from '@@/Link';
+import { AddButton } from '@@/buttons';
import { useRepeater } from '@@/datatables/useRepeater';
+import { DeleteButton } from '@@/buttons/DeleteButton';
import { createOwnershipColumn } from '../../components/datatable/createOwnershipColumn';
@@ -96,28 +96,16 @@ function TableActions({
return (
-
+ />
-
+ Add secret
);
diff --git a/app/react/docker/services/ListView/ServicesDatatable/TableActions.tsx b/app/react/docker/services/ListView/ServicesDatatable/TableActions.tsx
index 8593ead6f..147cc9b95 100644
--- a/app/react/docker/services/ListView/ServicesDatatable/TableActions.tsx
+++ b/app/react/docker/services/ListView/ServicesDatatable/TableActions.tsx
@@ -1,4 +1,4 @@
-import { Trash2, Plus, RefreshCw } from 'lucide-react';
+import { RefreshCw } from 'lucide-react';
import { useRouter } from '@uirouter/react';
import { ServiceViewModel } from '@/docker/models/service';
@@ -6,9 +6,8 @@ import { Authorized } from '@/react/hooks/useUser';
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
import { notifySuccess } from '@/portainer/services/notifications';
-import { Link } from '@@/Link';
-import { Button, ButtonGroup } from '@@/buttons';
-import { confirmDelete } from '@@/modals/confirm';
+import { AddButton, Button, ButtonGroup } from '@@/buttons';
+import { DeleteButton } from '@@/buttons/DeleteButton';
import { confirmServiceForceUpdate } from '../../common/update-service-modal';
@@ -46,28 +45,18 @@ export function TableActions({
)}
-
+ />
{isAddActionVisible && (
-
+ Add service
)}
@@ -97,14 +86,6 @@ export function TableActions({
}
async function handleRemove(selectedItems: Array) {
- const confirmed = await confirmDelete(
- 'Do you want to remove the selected service(s)? All the containers associated to the selected service(s) will be removed too.'
- );
-
- if (!confirmed) {
- return;
- }
-
removeMutation.mutate(
selectedItems.map((service) => service.Id),
{
diff --git a/app/react/docker/stacks/ListView/StacksDatatable/TableActions.tsx b/app/react/docker/stacks/ListView/StacksDatatable/TableActions.tsx
index f12542b3d..30693d39d 100644
--- a/app/react/docker/stacks/ListView/StacksDatatable/TableActions.tsx
+++ b/app/react/docker/stacks/ListView/StacksDatatable/TableActions.tsx
@@ -1,9 +1,7 @@
-import { Trash2, Plus } from 'lucide-react';
-
import { Authorized } from '@/react/hooks/useUser';
-import { Link } from '@@/Link';
-import { Button } from '@@/buttons';
+import { AddButton } from '@@/buttons';
+import { DeleteButton } from '@@/buttons/DeleteButton';
import { DecoratedStack } from './types';
@@ -17,28 +15,18 @@ export function TableActions({
return (
-
+ />
-
+
);
diff --git a/app/react/docker/volumes/ListView/VolumesDatatable/TableActions.tsx b/app/react/docker/volumes/ListView/VolumesDatatable/TableActions.tsx
index 49c63a5a9..93e0acd45 100644
--- a/app/react/docker/volumes/ListView/VolumesDatatable/TableActions.tsx
+++ b/app/react/docker/volumes/ListView/VolumesDatatable/TableActions.tsx
@@ -1,9 +1,7 @@
-import { Plus, Trash2 } from 'lucide-react';
-
import { Authorized } from '@/react/hooks/useUser';
-import { Link } from '@@/Link';
-import { Button } from '@@/buttons';
+import { AddButton } from '@@/buttons';
+import { DeleteButton } from '@@/buttons/DeleteButton';
import { DecoratedVolume } from '../types';
@@ -17,27 +15,15 @@ export function TableActions({
return (
-
+ />
-
+ Add volume
);
diff --git a/app/react/edge/edge-devices/WaitingRoomView/Datatable/TableActions.tsx b/app/react/edge/edge-devices/WaitingRoomView/Datatable/TableActions.tsx
index 81f01af17..98b2db24e 100644
--- a/app/react/edge/edge-devices/WaitingRoomView/Datatable/TableActions.tsx
+++ b/app/react/edge/edge-devices/WaitingRoomView/Datatable/TableActions.tsx
@@ -1,4 +1,4 @@
-import { Check, CheckCircle, Trash2 } from 'lucide-react';
+import { Check, CheckCircle } from 'lucide-react';
import { notifySuccess } from '@/portainer/services/notifications';
import { useDeleteEnvironmentsMutation } from '@/react/portainer/environments/queries/useDeleteEnvironmentsMutation';
@@ -7,10 +7,9 @@ import { withReactQuery } from '@/react-tools/withReactQuery';
import { useIsPureAdmin } from '@/react/hooks/useUser';
import { Button } from '@@/buttons';
-import { ModalType, openModal } from '@@/modals';
-import { confirm } from '@@/modals/confirm';
-import { buildConfirmButton } from '@@/modals/utils';
+import { openModal } from '@@/modals';
import { TooltipWithChildren } from '@@/Tip/TooltipWithChildren';
+import { DeleteButton } from '@@/buttons/DeleteButton';
import { useAssociateDeviceMutation, useLicenseOverused } from '../queries';
import { WaitingRoomEnvironment } from '../types';
@@ -36,14 +35,13 @@ export function TableActions({
return (
<>
-
+
d.Id),
{
diff --git a/app/react/edge/edge-stacks/ListView/EdgeStacksDatatable/TableActions.tsx b/app/react/edge/edge-stacks/ListView/EdgeStacksDatatable/TableActions.tsx
index 53a2254d6..579060dc6 100644
--- a/app/react/edge/edge-stacks/ListView/EdgeStacksDatatable/TableActions.tsx
+++ b/app/react/edge/edge-stacks/ListView/EdgeStacksDatatable/TableActions.tsx
@@ -1,11 +1,7 @@
-import { Trash2, Plus } from 'lucide-react';
-
import { notifySuccess } from '@/portainer/services/notifications';
-import { Button } from '@@/buttons';
-import { confirmDestructive } from '@@/modals/confirm';
-import { buildConfirmButton } from '@@/modals/utils';
-import { Link } from '@@/Link';
+import { AddButton } from '@@/buttons';
+import { DeleteButton } from '@@/buttons/DeleteButton';
import { useDeleteEdgeStacksMutation } from './useDeleteEdgeStacksMutation';
import { DecoratedEdgeStack } from './types';
@@ -19,39 +15,17 @@ export function TableActions({
return (
-
+ onConfirmed={() => handleRemove(selectedItems)}
+ confirmMessage="Are you sure you want to remove the selected Edge stack(s)?"
+ />
-
+
Add stack
);
async function handleRemove(selectedItems: Array) {
- const confirmed = await confirmDestructive({
- title: 'Are you sure?',
- message: 'Are you sure you want to remove the selected Edge stack(s)?',
- confirmButton: buildConfirmButton('Remove', 'danger'),
- });
-
- if (!confirmed) {
- return;
- }
-
const ids = selectedItems.map((item) => item.Id);
removeMutation.mutate(ids, {
onSuccess: () => {
diff --git a/app/react/kubernetes/applications/DetailsView/ApplicationDetailsWidget/ApplicationDetailsWidget.tsx b/app/react/kubernetes/applications/DetailsView/ApplicationDetailsWidget/ApplicationDetailsWidget.tsx
index 46ce98d90..fcb705263 100644
--- a/app/react/kubernetes/applications/DetailsView/ApplicationDetailsWidget/ApplicationDetailsWidget.tsx
+++ b/app/react/kubernetes/applications/DetailsView/ApplicationDetailsWidget/ApplicationDetailsWidget.tsx
@@ -1,4 +1,4 @@
-import { Pencil, Plus } from 'lucide-react';
+import { Pencil } from 'lucide-react';
import { useCurrentStateAndParams } from '@uirouter/react';
import { Pod } from 'kubernetes-types/core/v1';
@@ -7,7 +7,7 @@ import { useStackFile } from '@/react/common/stacks/stack.service';
import { useNamespaceQuery } from '@/react/kubernetes/namespaces/queries/useNamespaceQuery';
import { Widget, WidgetBody } from '@@/Widget';
-import { Button } from '@@/buttons';
+import { AddButton, Button } from '@@/buttons';
import { Link } from '@@/Link';
import { Icon } from '@@/Icon';
@@ -102,23 +102,15 @@ export function ApplicationDetailsWidget() {
/>
)}
{appStackFileQuery.data && (
-
-
-
+ Create template from application
+
)}
)}
diff --git a/app/react/kubernetes/applications/ListView/ApplicationsStacksDatatable/TableActions.tsx b/app/react/kubernetes/applications/ListView/ApplicationsStacksDatatable/TableActions.tsx
index fb7ef3546..fa7a3c647 100644
--- a/app/react/kubernetes/applications/ListView/ApplicationsStacksDatatable/TableActions.tsx
+++ b/app/react/kubernetes/applications/ListView/ApplicationsStacksDatatable/TableActions.tsx
@@ -1,8 +1,6 @@
-import { Trash2 } from 'lucide-react';
-
import { Authorized } from '@/react/hooks/useUser';
-import { Button } from '@@/buttons';
+import { DeleteButton } from '@@/buttons/DeleteButton';
import { KubernetesStack } from '../../types';
@@ -15,15 +13,12 @@ export function TableActions({
}) {
return (
-
+ />
);
}
diff --git a/app/react/kubernetes/configs/ListView/ConfigMapsDatatable/ConfigMapsDatatable.tsx b/app/react/kubernetes/configs/ListView/ConfigMapsDatatable/ConfigMapsDatatable.tsx
index 961661870..a96f71e63 100644
--- a/app/react/kubernetes/configs/ListView/ConfigMapsDatatable/ConfigMapsDatatable.tsx
+++ b/app/react/kubernetes/configs/ListView/ConfigMapsDatatable/ConfigMapsDatatable.tsx
@@ -1,5 +1,5 @@
import { useMemo } from 'react';
-import { FileCode, Plus, Trash2 } from 'lucide-react';
+import { FileCode } from 'lucide-react';
import { ConfigMap } from 'kubernetes-types/core/v1';
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
@@ -12,12 +12,12 @@ import { Application } from '@/react/kubernetes/applications/types';
import { pluralize } from '@/portainer/helpers/strings';
import { useNamespacesQuery } from '@/react/kubernetes/namespaces/queries/useNamespacesQuery';
import { Namespaces } from '@/react/kubernetes/namespaces/types';
+import { CreateFromManifestButton } from '@/react/kubernetes/components/CreateFromManifestButton';
import { Datatable, TableSettingsMenu } from '@@/datatables';
-import { confirmDelete } from '@@/modals/confirm';
-import { Button } from '@@/buttons';
-import { Link } from '@@/Link';
+import { AddButton } from '@@/buttons';
import { useTableState } from '@@/datatables/useTableState';
+import { DeleteButton } from '@@/buttons/DeleteButton';
import {
useConfigMapsForCluster,
@@ -139,16 +139,6 @@ function TableActions({
const deleteConfigMapMutation = useMutationDeleteConfigMaps(environmentId);
async function handleRemoveClick(configMaps: ConfigMap[]) {
- const confirmed = await confirmDelete(
- `Are you sure you want to remove the selected ${pluralize(
- configMaps.length,
- 'ConfigMap'
- )}?`
- );
- if (!confirmed) {
- return;
- }
-
const configMapsToDelete = configMaps.map((configMap) => ({
namespace: configMap.metadata?.namespace ?? '',
name: configMap.metadata?.name ?? '',
@@ -159,41 +149,30 @@ function TableActions({
return (
-
-
-
-
-
+
+
-
-
+ />
);
}
diff --git a/app/react/kubernetes/configs/ListView/SecretsDatatable/SecretsDatatable.tsx b/app/react/kubernetes/configs/ListView/SecretsDatatable/SecretsDatatable.tsx
index 7f0699f9d..318c3d4ec 100644
--- a/app/react/kubernetes/configs/ListView/SecretsDatatable/SecretsDatatable.tsx
+++ b/app/react/kubernetes/configs/ListView/SecretsDatatable/SecretsDatatable.tsx
@@ -1,5 +1,5 @@
import { useMemo } from 'react';
-import { Lock, Plus, Trash2 } from 'lucide-react';
+import { Lock } from 'lucide-react';
import { Secret } from 'kubernetes-types/core/v1';
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
@@ -12,12 +12,12 @@ import { Application } from '@/react/kubernetes/applications/types';
import { pluralize } from '@/portainer/helpers/strings';
import { useNamespacesQuery } from '@/react/kubernetes/namespaces/queries/useNamespacesQuery';
import { Namespaces } from '@/react/kubernetes/namespaces/types';
+import { CreateFromManifestButton } from '@/react/kubernetes/components/CreateFromManifestButton';
import { Datatable, TableSettingsMenu } from '@@/datatables';
-import { confirmDelete } from '@@/modals/confirm';
-import { Button } from '@@/buttons';
-import { Link } from '@@/Link';
+import { AddButton } from '@@/buttons';
import { useTableState } from '@@/datatables/useTableState';
+import { DeleteButton } from '@@/buttons/DeleteButton';
import {
useSecretsForCluster,
@@ -135,16 +135,6 @@ function TableActions({ selectedItems }: { selectedItems: SecretRowData[] }) {
const deleteSecretMutation = useMutationDeleteSecrets(environmentId);
async function handleRemoveClick(secrets: SecretRowData[]) {
- const confirmed = await confirmDelete(
- `Are you sure you want to remove the selected ${pluralize(
- secrets.length,
- 'secret'
- )}?`
- );
- if (!confirmed) {
- return;
- }
-
const secretsToDelete = secrets.map((secret) => ({
namespace: secret.metadata?.namespace ?? '',
name: secret.metadata?.name ?? '',
@@ -155,41 +145,28 @@ function TableActions({ selectedItems }: { selectedItems: SecretRowData[] }) {
return (
-
-
-
-
-
+
-
-
+ />
);
}
diff --git a/app/react/kubernetes/ingresses/IngressDatatable/IngressDatatable.tsx b/app/react/kubernetes/ingresses/IngressDatatable/IngressDatatable.tsx
index 6e9496f94..062487dbd 100644
--- a/app/react/kubernetes/ingresses/IngressDatatable/IngressDatatable.tsx
+++ b/app/react/kubernetes/ingresses/IngressDatatable/IngressDatatable.tsx
@@ -1,4 +1,3 @@
-import { Plus, Trash2 } from 'lucide-react';
import { useRouter } from '@uirouter/react';
import { useMemo } from 'react';
@@ -9,16 +8,16 @@ import { DefaultDatatableSettings } from '@/react/kubernetes/datatables/DefaultD
import { createStore } from '@/react/kubernetes/datatables/default-kube-datatable-store';
import { SystemResourceDescription } from '@/react/kubernetes/datatables/SystemResourceDescription';
-import { confirmDelete } from '@@/modals/confirm';
import { Datatable, TableSettingsMenu } from '@@/datatables';
-import { Button } from '@@/buttons';
-import { Link } from '@@/Link';
+import { AddButton } from '@@/buttons';
import { useTableState } from '@@/datatables/useTableState';
+import { DeleteButton } from '@@/buttons/DeleteButton';
import { DeleteIngressesRequest, Ingress } from '../types';
import { useDeleteIngresses, useIngresses } from '../queries';
import { useNamespacesQuery } from '../../namespaces/queries/useNamespacesQuery';
import { Namespaces } from '../../namespaces/types';
+import { CreateFromManifestButton } from '../../components/CreateFromManifestButton';
import { columns } from './columns';
@@ -111,38 +110,20 @@ export function IngressDatatable() {
function tableActions(selectedFlatRows: Ingress[]) {
return (
-
-
-
-
+
+ handleRemoveClick(selectedFlatRows)}
+ data-cy="k8sSecret-removeSecretButton"
+ confirmMessage="Are you sure you want to delete the selected ingresses?"
+ />
-
-
-
-
-
-
-
-
-
-
-
+
+ Add with form
+
+
+
+
);
}
@@ -152,13 +133,6 @@ export function IngressDatatable() {
}
async function handleRemoveClick(ingresses: SelectedIngress[]) {
- const confirmed = await confirmDelete(
- 'Are you sure you want to delete the selected ingresses?'
- );
- if (!confirmed) {
- return null;
- }
-
const payload: DeleteIngressesRequest = {} as DeleteIngressesRequest;
ingresses.forEach((ingress) => {
payload[ingress.Namespace] = payload[ingress.Namespace] || [];
@@ -173,6 +147,5 @@ export function IngressDatatable() {
},
}
);
- return ingresses;
}
}
diff --git a/app/react/kubernetes/ingresses/style.css b/app/react/kubernetes/ingresses/style.css
index 928aeecb8..7f189cc10 100644
--- a/app/react/kubernetes/ingresses/style.css
+++ b/app/react/kubernetes/ingresses/style.css
@@ -7,7 +7,6 @@
background-color: var(--bg-body-color);
}
-.ingressDatatable-actions button > span,
.anntation-actions button > span,
.rules-action button > span,
.rule button > span {
diff --git a/app/react/kubernetes/services/ServicesView/ServicesDatatable/ServicesDatatable.tsx b/app/react/kubernetes/services/ServicesView/ServicesDatatable/ServicesDatatable.tsx
index fe3389ba1..23d04db35 100644
--- a/app/react/kubernetes/services/ServicesView/ServicesDatatable/ServicesDatatable.tsx
+++ b/app/react/kubernetes/services/ServicesView/ServicesDatatable/ServicesDatatable.tsx
@@ -1,8 +1,8 @@
-import { useMemo } from 'react';
-import { Shuffle, Trash2 } from 'lucide-react';
+import { Shuffle } from 'lucide-react';
import { useRouter } from '@uirouter/react';
import clsx from 'clsx';
import { Row } from '@tanstack/react-table';
+import { useMemo } from 'react';
import { Namespaces } from '@/react/kubernetes/namespaces/types';
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
@@ -12,12 +12,11 @@ import { pluralize } from '@/portainer/helpers/strings';
import { DefaultDatatableSettings } from '@/react/kubernetes/datatables/DefaultDatatableSettings';
import { SystemResourceDescription } from '@/react/kubernetes/datatables/SystemResourceDescription';
import { useNamespacesQuery } from '@/react/kubernetes/namespaces/queries/useNamespacesQuery';
+import { CreateFromManifestButton } from '@/react/kubernetes/components/CreateFromManifestButton';
import { Datatable, Table, TableSettingsMenu } from '@@/datatables';
-import { confirmDelete } from '@@/modals/confirm';
-import { Button } from '@@/buttons';
-import { Link } from '@@/Link';
import { useTableState } from '@@/datatables/useTableState';
+import { DeleteButton } from '@@/buttons/DeleteButton';
import {
useMutationDeleteServices,
@@ -94,7 +93,7 @@ export function ServicesDatatable() {
);
}
-// useServicesRowData appends the `isSyetem` property to the service data
+// useServicesRowData appends the `isSystem` property to the service data
function useServicesRowData(
services: Service[],
namespaces?: Namespaces
@@ -136,26 +135,33 @@ function TableActions({ selectedItems }: TableActionsProps) {
const deleteServicesMutation = useMutationDeleteServices(environmentId);
const router = useRouter();
- async function handleRemoveClick(services: SelectedService[]) {
- const confirmed = await confirmDelete(
- <>
- {`Are you sure you want to remove the selected ${pluralize(
- services.length,
- 'service'
- )}?`}
-
- {services.map((s, index) => (
- -
- {s.Namespace}/{s.Name}
-
- ))}
-
- >
- );
- if (!confirmed) {
- return null;
- }
+ return (
+
+ handleRemoveClick(selectedItems)}
+ confirmMessage={
+ <>
+ {`Are you sure you want to remove the selected ${pluralize(
+ selectedItems.length,
+ 'service'
+ )}?`}
+
+ {selectedItems.map((s, index) => (
+ -
+ {s.Namespace}/{s.Name}
+
+ ))}
+
+ >
+ }
+ />
+
+
+ );
+
+ async function handleRemoveClick(services: SelectedService[]) {
const payload: Record = {};
services.forEach((service) => {
payload[service.Namespace] = payload[service.Namespace] || [];
@@ -181,32 +187,5 @@ function TableActions({ selectedItems }: TableActionsProps) {
},
}
);
- return services;
}
-
- return (
-
-
-
-
-
-
-
-
-
- );
}
diff --git a/app/react/portainer/account/AccountView/HelmRepositoryDatatable/HelmRepositoryDatatableActions.tsx b/app/react/portainer/account/AccountView/HelmRepositoryDatatable/HelmRepositoryDatatableActions.tsx
index da73d8490..8a2408e39 100644
--- a/app/react/portainer/account/AccountView/HelmRepositoryDatatable/HelmRepositoryDatatableActions.tsx
+++ b/app/react/portainer/account/AccountView/HelmRepositoryDatatable/HelmRepositoryDatatableActions.tsx
@@ -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 (
-
-
-
-
Add Helm repository
-
+ >
);
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();
diff --git a/app/react/portainer/environments/ListView/EnvironmentsDatatable.tsx b/app/react/portainer/environments/ListView/EnvironmentsDatatable.tsx
index 1b17a818c..02ea17876 100644
--- a/app/react/portainer/environments/ListView/EnvironmentsDatatable.tsx
+++ b/app/react/portainer/environments/ListView/EnvironmentsDatatable.tsx
@@ -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({
{isBE && (
-
+
)}
-
-
-
+
+
+ Add environment
+
)}
/>
diff --git a/app/react/portainer/environments/ListView/ImportFdoDeviceButton.tsx b/app/react/portainer/environments/ListView/ImportFdoDeviceButton.tsx
index 8683155da..d42efe20a 100644
--- a/app/react/portainer/environments/ListView/ImportFdoDeviceButton.tsx
+++ b/app/react/portainer/environments/ListView/ImportFdoDeviceButton.tsx
@@ -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 (
-
+
+
+ Import FDO device
+
+
);
}
diff --git a/app/react/portainer/environments/update-schedules/ListView/ListView.tsx b/app/react/portainer/environments/update-schedules/ListView/ListView.tsx
index 35b35a02d..c0d16fea2 100644
--- a/app/react/portainer/environments/update-schedules/ListView/ListView.tsx
+++ b/app/react/portainer/environments/update-schedules/ListView/ListView.tsx
@@ -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 (
<>
-
-
-
-
-
+ confirmMessage="Are you sure you want to remove these schedules?"
+ />
+ Add update & rollback schedule
>
);
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');
diff --git a/app/react/portainer/environments/wizard/EnvironmentsCreationView/EnvironmentsCreationView.tsx b/app/react/portainer/environments/wizard/EnvironmentsCreationView/EnvironmentsCreationView.tsx
index 427d4eb44..6f1bf1c1f 100644
--- a/app/react/portainer/environments/wizard/EnvironmentsCreationView/EnvironmentsCreationView.tsx
+++ b/app/react/portainer/environments/wizard/EnvironmentsCreationView/EnvironmentsCreationView.tsx
@@ -33,7 +33,7 @@ import { WizardEndpointsList } from './WizardEndpointsList';
export function EnvironmentCreationView() {
const {
- params: { localEndpointId: localEndpointIdParam },
+ params: { localEndpointId: localEndpointIdParam, referrer },
} = useCurrentStateAndParams();
const [environmentIds, setEnvironmentIds] = useState(() => {
@@ -130,8 +130,7 @@ export function EnvironmentCreationView() {
])
),
});
- if (localStorage.getItem('wizardReferrer') === 'environments') {
- localStorage.removeItem('wizardReferrer');
+ if (referrer === 'environments') {
router.stateService.go('portainer.endpoints');
return;
}
diff --git a/app/react/portainer/notifications/NotificationsView.tsx b/app/react/portainer/notifications/NotificationsView.tsx
index 6024197d4..afb3b69d0 100644
--- a/app/react/portainer/notifications/NotificationsView.tsx
+++ b/app/react/portainer/notifications/NotificationsView.tsx
@@ -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 (
-
+ confirmMessage="Are you sure you want to remove the selected notifications?"
+ />
);
function handleRemove() {
diff --git a/app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/FDOProfilesDatatableActions.tsx b/app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/FDOProfilesDatatableActions.tsx
index a3b7759aa..1b78e96b5 100644
--- a/app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/FDOProfilesDatatableActions.tsx
+++ b/app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/FDOProfilesDatatableActions.tsx
@@ -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 (
-
+ <>
-
-
+ 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 {
diff --git a/app/react/portainer/templates/app-templates/AppTemplatesListItem.test.tsx b/app/react/portainer/templates/app-templates/AppTemplatesListItem.test.tsx
index e5221b8fa..0857d3ef2 100644
--- a/app/react/portainer/templates/app-templates/AppTemplatesListItem.test.tsx
+++ b/app/react/portainer/templates/app-templates/AppTemplatesListItem.test.tsx
@@ -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(
-
- );
+ 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