diff --git a/package.json b/package.json index d5fe2ccad..210594ccc 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "serve": "./entrypoint", "test-js": "react-scripts test src/ --watchAll=false"}, "dependencies": { - "@canonical/react-components": "0.47.1", + "@canonical/react-components": "0.47.3", "@monaco-editor/react": "4.6.0", "@tanstack/react-query": "5.17.15", "@use-it/event-listener": "0.1.7", @@ -36,7 +36,7 @@ "react": "18.2.0", "react-cytoscapejs": "2.0.0", "react-dom": "18.2.0", - "react-router-dom": "6.17.0", + "react-router-dom": "6.21.3", "react-scripts": "5.0.1", "react-useportal": "1.0.19", "serve": "14.2.1", diff --git a/src/Root.tsx b/src/Root.tsx index e99a8776e..532edf715 100644 --- a/src/Root.tsx +++ b/src/Root.tsx @@ -1,6 +1,9 @@ import React, { FC } from "react"; import Navigation from "components/Navigation"; -import { NotificationProvider } from "@canonical/react-components"; +import { + NotificationProvider, + QueuedNotification, +} from "@canonical/react-components"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import Panels from "components/Panels"; import { AuthProvider } from "context/auth"; @@ -11,13 +14,16 @@ import Events from "pages/instances/Events"; import App from "./App"; import ErrorBoundary from "components/ErrorBoundary"; import ErrorPage from "components/ErrorPage"; +import { useLocation } from "react-router-dom"; const queryClient = new QueryClient(); const Root: FC = () => { + const location = useLocation() as QueuedNotification; + return ( - + diff --git a/src/pages/cluster/ClusterGroupForm.tsx b/src/pages/cluster/ClusterGroupForm.tsx index 42a974518..81d79c2eb 100644 --- a/src/pages/cluster/ClusterGroupForm.tsx +++ b/src/pages/cluster/ClusterGroupForm.tsx @@ -5,6 +5,7 @@ import { Form, Input, Row, + success, useNotify, } from "@canonical/react-components"; import { useQuery, useQueryClient } from "@tanstack/react-query"; @@ -84,9 +85,7 @@ const ClusterGroupForm: FC = ({ group }) => { const verb = group ? "saved" : "created"; navigate( `/ui/cluster/groups/detail/${values.name}`, - notify.queue( - notify.success(`Cluster group ${values.name} ${verb}.`), - ), + notify.queue(success(`Cluster group ${values.name} ${verb}.`)), ); }) .catch((e: Error) => { diff --git a/src/pages/cluster/actions/DeleteClusterGroupBtn.tsx b/src/pages/cluster/actions/DeleteClusterGroupBtn.tsx index 1decd5c28..23203bcb8 100644 --- a/src/pages/cluster/actions/DeleteClusterGroupBtn.tsx +++ b/src/pages/cluster/actions/DeleteClusterGroupBtn.tsx @@ -4,7 +4,11 @@ import ItemName from "components/ItemName"; import { deleteClusterGroup } from "api/cluster"; import { queryKeys } from "util/queryKeys"; import { useQueryClient } from "@tanstack/react-query"; -import { ConfirmationButton, useNotify } from "@canonical/react-components"; +import { + ConfirmationButton, + success, + useNotify, +} from "@canonical/react-components"; interface Props { group: string; @@ -22,7 +26,7 @@ const DeleteClusterGroupBtn: FC = ({ group }) => { .then(() => { navigate( `/ui/cluster`, - notify.queue(notify.success(`Cluster group ${group} deleted.`)), + notify.queue(success(`Cluster group ${group} deleted.`)), ); }) .catch((e) => { diff --git a/src/pages/instances/InstanceDetailHeader.tsx b/src/pages/instances/InstanceDetailHeader.tsx index ada6116a6..5e7c644c5 100644 --- a/src/pages/instances/InstanceDetailHeader.tsx +++ b/src/pages/instances/InstanceDetailHeader.tsx @@ -8,7 +8,7 @@ import InstanceStateActions from "pages/instances/actions/InstanceStateActions"; import { useFormik } from "formik"; import * as Yup from "yup"; import { checkDuplicateName } from "util/helpers"; -import { useNotify } from "@canonical/react-components"; +import { success, useNotify } from "@canonical/react-components"; import { useEventQueue } from "context/eventQueue"; interface Props { @@ -59,7 +59,7 @@ const InstanceDetailHeader: FC = ({ name, instance, project }) => { () => { navigate( `/ui/project/${project}/instances/detail/${values.name}`, - notify.queue(notify.success("Instance renamed.")), + notify.queue(success("Instance renamed.")), ); void formik.setFieldValue("isRenaming", false); }, diff --git a/src/pages/instances/actions/DeleteInstanceBtn.tsx b/src/pages/instances/actions/DeleteInstanceBtn.tsx index d377c8b7b..819814cf9 100644 --- a/src/pages/instances/actions/DeleteInstanceBtn.tsx +++ b/src/pages/instances/actions/DeleteInstanceBtn.tsx @@ -8,6 +8,7 @@ import { useDeleteIcon } from "context/useDeleteIcon"; import { ConfirmationButton, Icon, + success, useNotify, } from "@canonical/react-components"; import classnames from "classnames"; @@ -38,7 +39,7 @@ const DeleteInstanceBtn: FC = ({ instance }) => { }); navigate( `/ui/project/${instance.project}/instances`, - notify.queue(notify.success(`Instance ${instance.name} deleted.`)), + notify.queue(success(`Instance ${instance.name} deleted.`)), ); }, (msg) => diff --git a/src/pages/networks/CreateNetwork.tsx b/src/pages/networks/CreateNetwork.tsx index 6b7410b2d..8a6dc5562 100644 --- a/src/pages/networks/CreateNetwork.tsx +++ b/src/pages/networks/CreateNetwork.tsx @@ -1,5 +1,5 @@ import React, { FC, useState } from "react"; -import { Button, useNotify } from "@canonical/react-components"; +import { Button, success, useNotify } from "@canonical/react-components"; import { useFormik } from "formik"; import * as Yup from "yup"; import { useQuery, useQueryClient } from "@tanstack/react-query"; @@ -91,7 +91,7 @@ const CreateNetwork: FC = () => { }); navigate( `/ui/project/${project}/networks`, - notify.queue(notify.success(`Network ${values.name} created.`)), + notify.queue(success(`Network ${values.name} created.`)), ); }) .catch((e) => { diff --git a/src/pages/networks/NetworkDetailHeader.tsx b/src/pages/networks/NetworkDetailHeader.tsx index d62ae45d9..e34ebac12 100644 --- a/src/pages/networks/NetworkDetailHeader.tsx +++ b/src/pages/networks/NetworkDetailHeader.tsx @@ -7,7 +7,7 @@ import { checkDuplicateName } from "util/helpers"; import { LxdNetwork } from "types/network"; import { renameNetwork } from "api/networks"; import DeleteNetworkBtn from "pages/networks/actions/DeleteNetworkBtn"; -import { useNotify } from "@canonical/react-components"; +import { success, useNotify } from "@canonical/react-components"; interface Props { name: string; @@ -48,7 +48,7 @@ const NetworkDetailHeader: FC = ({ name, network, project }) => { .then(() => { navigate( `/ui/project/${project}/networks/detail/${values.name}`, - notify.queue(notify.success("Network renamed.")), + notify.queue(success("Network renamed.")), ); void formik.setFieldValue("isRenaming", false); }) diff --git a/src/pages/networks/actions/DeleteNetworkBtn.tsx b/src/pages/networks/actions/DeleteNetworkBtn.tsx index c8b7ce272..ad863dcf4 100644 --- a/src/pages/networks/actions/DeleteNetworkBtn.tsx +++ b/src/pages/networks/actions/DeleteNetworkBtn.tsx @@ -5,7 +5,11 @@ import { LxdNetwork } from "types/network"; import { deleteNetwork } from "api/networks"; import { queryKeys } from "util/queryKeys"; import { useQueryClient } from "@tanstack/react-query"; -import { ConfirmationButton, useNotify } from "@canonical/react-components"; +import { + ConfirmationButton, + success, + useNotify, +} from "@canonical/react-components"; interface Props { network: LxdNetwork; @@ -30,7 +34,7 @@ const DeleteNetworkBtn: FC = ({ network, project }) => { }); navigate( `/ui/project/${project}/networks`, - notify.queue(notify.success(`Network ${network.name} deleted.`)), + notify.queue(success(`Network ${network.name} deleted.`)), ); }) .catch((e) => { diff --git a/src/pages/profiles/CreateProfile.tsx b/src/pages/profiles/CreateProfile.tsx index 0babba144..952d6d0c2 100644 --- a/src/pages/profiles/CreateProfile.tsx +++ b/src/pages/profiles/CreateProfile.tsx @@ -5,6 +5,7 @@ import { Form, Notification, Row, + success, useNotify, } from "@canonical/react-components"; import { useFormik } from "formik"; @@ -110,7 +111,7 @@ const CreateProfile: FC = () => { .then(() => { navigate( `/ui/project/${project}/profiles`, - notify.queue(notify.success(`Profile ${values.name} created.`)), + notify.queue(success(`Profile ${values.name} created.`)), ); }) .catch((e: Error) => { diff --git a/src/pages/profiles/ProfileDetailHeader.tsx b/src/pages/profiles/ProfileDetailHeader.tsx index e66df6395..c6e633348 100644 --- a/src/pages/profiles/ProfileDetailHeader.tsx +++ b/src/pages/profiles/ProfileDetailHeader.tsx @@ -7,7 +7,7 @@ import { renameProfile } from "api/profiles"; import { useFormik } from "formik"; import * as Yup from "yup"; import { checkDuplicateName } from "util/helpers"; -import { useNotify } from "@canonical/react-components"; +import { success, useNotify } from "@canonical/react-components"; interface Props { name: string; @@ -54,7 +54,7 @@ const ProfileDetailHeader: FC = ({ .then(() => { navigate( `/ui/project/${project}/profiles/detail/${values.name}`, - notify.queue(notify.success("Profile renamed.")), + notify.queue(success("Profile renamed.")), ); void formik.setFieldValue("isRenaming", false); }) diff --git a/src/pages/profiles/actions/DeleteProfileBtn.tsx b/src/pages/profiles/actions/DeleteProfileBtn.tsx index 123658f5b..0218e8f47 100644 --- a/src/pages/profiles/actions/DeleteProfileBtn.tsx +++ b/src/pages/profiles/actions/DeleteProfileBtn.tsx @@ -7,6 +7,7 @@ import { useDeleteIcon } from "context/useDeleteIcon"; import { ConfirmationButton, Icon, + success, useNotify, } from "@canonical/react-components"; import classnames from "classnames"; @@ -39,7 +40,7 @@ const DeleteProfileBtn: FC = ({ }); navigate( `/ui/project/${project}/profiles`, - notify.queue(notify.success(`Profile ${profile.name} deleted.`)), + notify.queue(success(`Profile ${profile.name} deleted.`)), ); }) .catch((e) => { diff --git a/src/pages/projects/CreateProject.tsx b/src/pages/projects/CreateProject.tsx index 0a921be3a..38ccc4630 100644 --- a/src/pages/projects/CreateProject.tsx +++ b/src/pages/projects/CreateProject.tsx @@ -1,5 +1,5 @@ import React, { FC, useEffect, useState } from "react"; -import { Button, useNotify } from "@canonical/react-components"; +import { Button, success, useNotify } from "@canonical/react-components"; import { useFormik } from "formik"; import * as Yup from "yup"; import { useQueryClient } from "@tanstack/react-query"; @@ -99,7 +99,7 @@ const CreateProject: FC = () => { .then(() => { navigate( `/ui/project/${values.name}/instances`, - notify.queue(notify.success(`Project ${values.name} created.`)), + notify.queue(success(`Project ${values.name} created.`)), ); }) .catch((e: Error) => { diff --git a/src/pages/projects/ProjectConfigurationHeader.tsx b/src/pages/projects/ProjectConfigurationHeader.tsx index 8648f427e..83938960d 100644 --- a/src/pages/projects/ProjectConfigurationHeader.tsx +++ b/src/pages/projects/ProjectConfigurationHeader.tsx @@ -7,7 +7,7 @@ import * as Yup from "yup"; import { useFormik } from "formik"; import { checkDuplicateName } from "util/helpers"; import DeleteProjectBtn from "./actions/DeleteProjectBtn"; -import { useNotify } from "@canonical/react-components"; +import { success, useNotify } from "@canonical/react-components"; import HelpLink from "components/HelpLink"; import { useEventQueue } from "context/eventQueue"; import { useDocs } from "context/useDocs"; @@ -53,7 +53,7 @@ const ProjectConfigurationHeader: FC = ({ project }) => { () => { navigate( `/ui/project/${values.name}/configuration`, - notify.queue(notify.success("Project renamed.")), + notify.queue(success("Project renamed.")), ); void formik.setFieldValue("isRenaming", false); }, diff --git a/src/pages/projects/actions/DeleteProjectBtn.tsx b/src/pages/projects/actions/DeleteProjectBtn.tsx index 4ccdd1d3c..f42ba7622 100644 --- a/src/pages/projects/actions/DeleteProjectBtn.tsx +++ b/src/pages/projects/actions/DeleteProjectBtn.tsx @@ -10,6 +10,7 @@ import { useDeleteIcon } from "context/useDeleteIcon"; import { ConfirmationButton, Icon, + success, useNotify, } from "@canonical/react-components"; import classnames from "classnames"; @@ -43,7 +44,7 @@ const DeleteProjectBtn: FC = ({ project }) => { .then(() => { navigate( `/ui/project/default/instances`, - notify.queue(notify.success(`Project ${project.name} deleted.`)), + notify.queue(success(`Project ${project.name} deleted.`)), ); }) .catch((e) => { diff --git a/src/pages/storage/StoragePoolHeader.tsx b/src/pages/storage/StoragePoolHeader.tsx index 8b55fa502..f5cff1f89 100644 --- a/src/pages/storage/StoragePoolHeader.tsx +++ b/src/pages/storage/StoragePoolHeader.tsx @@ -7,7 +7,7 @@ import { LxdStoragePool } from "types/storage"; import { renameStoragePool } from "api/storage-pools"; import DeleteStoragePoolBtn from "pages/storage/actions/DeleteStoragePoolBtn"; import { testDuplicateStoragePoolName } from "util/storagePool"; -import { useNotify } from "@canonical/react-components"; +import { success, useNotify } from "@canonical/react-components"; interface Props { name: string; @@ -42,7 +42,7 @@ const StoragePoolHeader: FC = ({ name, pool, project }) => { .then(() => { navigate( `/ui/project/${project}/storage/detail/${values.name}`, - notify.queue(notify.success("Storage pool renamed.")), + notify.queue(success("Storage pool renamed.")), ); void formik.setFieldValue("isRenaming", false); }) diff --git a/src/pages/storage/StorageVolumeHeader.tsx b/src/pages/storage/StorageVolumeHeader.tsx index b1e790c96..3ff487e85 100644 --- a/src/pages/storage/StorageVolumeHeader.tsx +++ b/src/pages/storage/StorageVolumeHeader.tsx @@ -6,7 +6,7 @@ import * as Yup from "yup"; import { LxdStorageVolume } from "types/storage"; import { renameStorageVolume } from "api/storage-pools"; import { testDuplicateStorageVolumeName } from "util/storageVolume"; -import { useNotify } from "@canonical/react-components"; +import { success, useNotify } from "@canonical/react-components"; import DeleteStorageVolumeBtn from "pages/storage/actions/DeleteStorageVolumeBtn"; interface Props { @@ -49,7 +49,7 @@ const StorageVolumeHeader: FC = ({ volume, project }) => { .then(() => { navigate( `/ui/project/${project}/storage/detail/${volume.pool}/${volume.type}/${values.name}`, - notify.queue(notify.success("Storage volume renamed.")), + notify.queue(success("Storage volume renamed.")), ); void formik.setFieldValue("isRenaming", false); }) diff --git a/src/pages/storage/StorageVolumeSnapshots.tsx b/src/pages/storage/StorageVolumeSnapshots.tsx index d30d98361..2af0d86c4 100644 --- a/src/pages/storage/StorageVolumeSnapshots.tsx +++ b/src/pages/storage/StorageVolumeSnapshots.tsx @@ -1,4 +1,4 @@ -import React, { FC, ReactNode, useEffect, useState } from "react"; +import React, { FC, useEffect, useState } from "react"; import { EmptyState, Icon, @@ -62,14 +62,6 @@ const StorageVolumeSnapshots: FC = ({ volume }) => { }), }); - const onSuccess = (message: string) => { - notify.queue(notify.success(message)); - }; - - const onFailure = (title: string, error: unknown, message?: ReactNode) => { - notify.failure(title, error, message); - }; - const snapshotsDisabled = isSnapshotsDisabled(project); useEffect(() => { @@ -129,12 +121,7 @@ const StorageVolumeSnapshots: FC = ({ volume }) => { const rows = filteredSnapshots.map((snapshot) => { const actions = ( - + ); return { @@ -227,8 +214,6 @@ const StorageVolumeSnapshots: FC = ({ volume }) => { = ({ volume }) => { snapshotNames={selectedNames} onStart={() => setProcessingNames(selectedNames)} onFinish={() => setProcessingNames([])} - onSuccess={onSuccess} - onFailure={onFailure} /> )} @@ -323,8 +306,6 @@ const StorageVolumeSnapshots: FC = ({ volume }) => {

= ({ }); navigate( `/ui/project/${project}/storage`, - notify.queue(notify.success(`Storage pool ${pool.name} deleted.`)), + notify.queue(success(`Storage pool ${pool.name} deleted.`)), ); }) .catch((e) => { diff --git a/src/pages/storage/actions/snapshots/VolumeConfigureSnapshotBtn.tsx b/src/pages/storage/actions/snapshots/VolumeConfigureSnapshotBtn.tsx index 14d05d2b8..cd212fc08 100644 --- a/src/pages/storage/actions/snapshots/VolumeConfigureSnapshotBtn.tsx +++ b/src/pages/storage/actions/snapshots/VolumeConfigureSnapshotBtn.tsx @@ -1,4 +1,4 @@ -import React, { FC, ReactNode } from "react"; +import React, { FC } from "react"; import usePortal from "react-useportal"; import { Button } from "@canonical/react-components"; import VolumeConfigureSnapshotModal from "./VolumeConfigureSnapshotModal"; @@ -6,16 +6,12 @@ import { LxdStorageVolume } from "types/storage"; interface Props { volume: LxdStorageVolume; - onSuccess: (message: string) => void; - onFailure: (title: string, e: unknown, message?: ReactNode) => void; isDisabled?: boolean; className?: string; } const VolumeConfigureSnapshotBtn: FC = ({ volume, - onSuccess, - onFailure, isDisabled, className, }) => { @@ -26,12 +22,7 @@ const VolumeConfigureSnapshotBtn: FC = ({ {isOpen && (
- +
)} diff --git a/src/pages/storage/actions/snapshots/VolumeConfigureSnapshotModal.tsx b/src/pages/storage/actions/snapshots/VolumeConfigureSnapshotModal.tsx index 892ef84af..f1c262666 100644 --- a/src/pages/storage/actions/snapshots/VolumeConfigureSnapshotModal.tsx +++ b/src/pages/storage/actions/snapshots/VolumeConfigureSnapshotModal.tsx @@ -1,5 +1,5 @@ import React, { FC, KeyboardEvent } from "react"; -import { Button, Modal } from "@canonical/react-components"; +import { Button, Modal, useNotify } from "@canonical/react-components"; import { useFormik } from "formik"; import { queryKeys } from "util/queryKeys"; import { useQueryClient } from "@tanstack/react-query"; @@ -16,16 +16,10 @@ import StorageVolumeFormSnapshots from "pages/storage/forms/StorageVolumeFormSna interface Props { volume: LxdStorageVolume; close: () => void; - onSuccess: (message: string) => void; - onFailure: (title: string, e: unknown) => void; } -const VolumeConfigureSnapshotModal: FC = ({ - volume, - close, - onSuccess, - onFailure, -}) => { +const VolumeConfigureSnapshotModal: FC = ({ volume, close }) => { + const notify = useNotify(); const queryClient = useQueryClient(); const formik = useFormik({ @@ -37,7 +31,7 @@ const VolumeConfigureSnapshotModal: FC = ({ etag: volume.etag, }) .then(() => { - onSuccess("Configuration updated."); + notify.success("Configuration updated."); void queryClient.invalidateQueries({ queryKey: [queryKeys.storage], predicate: (query) => @@ -46,7 +40,7 @@ const VolumeConfigureSnapshotModal: FC = ({ }); }) .catch((e: Error) => { - onFailure("Configuration update failed", e); + notify.failure("Configuration update failed", e); }) .finally(() => { close(); diff --git a/src/pages/storage/actions/snapshots/VolumeSnapshotActions.tsx b/src/pages/storage/actions/snapshots/VolumeSnapshotActions.tsx index 083b602a4..016a912e8 100644 --- a/src/pages/storage/actions/snapshots/VolumeSnapshotActions.tsx +++ b/src/pages/storage/actions/snapshots/VolumeSnapshotActions.tsx @@ -6,7 +6,12 @@ import { } from "api/volume-snapshots"; import { useQueryClient } from "@tanstack/react-query"; import { queryKeys } from "util/queryKeys"; -import { ConfirmationButton, Icon, List } from "@canonical/react-components"; +import { + ConfirmationButton, + Icon, + List, + useNotify, +} from "@canonical/react-components"; import classnames from "classnames"; import ItemName from "components/ItemName"; import { useEventQueue } from "context/eventQueue"; @@ -15,17 +20,11 @@ import VolumeEditSnapshotBtn from "./VolumeEditSnapshotBtn"; interface Props { volume: LxdStorageVolume; snapshot: LxdVolumeSnapshot; - onSuccess: (message: string) => void; - onFailure: (title: string, error: Error) => void; } -const VolumeSnapshotActions: FC = ({ - volume, - snapshot, - onSuccess, - onFailure, -}) => { +const VolumeSnapshotActions: FC = ({ volume, snapshot }) => { const eventQueue = useEventQueue(); + const notify = useNotify(); const [isDeleting, setDeleting] = useState(false); const [isRestoring, setRestoring] = useState(false); const queryClient = useQueryClient(); @@ -35,8 +34,8 @@ const VolumeSnapshotActions: FC = ({ void deleteVolumeSnapshot(volume, snapshot).then((operation) => eventQueue.set( operation.metadata.id, - () => onSuccess(`Snapshot ${snapshot.name} deleted`), - (msg) => onFailure("Snapshot deletion failed", new Error(msg)), + () => notify.success(`Snapshot ${snapshot.name} deleted`), + (msg) => notify.failure("Snapshot deletion failed", new Error(msg)), () => { setDeleting(false); void queryClient.invalidateQueries({ @@ -53,10 +52,10 @@ const VolumeSnapshotActions: FC = ({ setRestoring(true); void restoreVolumeSnapshot(volume, snapshot) .then(() => { - onSuccess(`Snapshot ${snapshot.name} restored`); + notify.success(`Snapshot ${snapshot.name} restored`); }) .catch((error: Error) => { - onFailure("Snapshot restore failed", error); + notify.failure("Snapshot restore failed", error); }) .finally(() => { setRestoring(false); diff --git a/src/pages/storage/actions/snapshots/VolumeSnapshotBulkDelete.tsx b/src/pages/storage/actions/snapshots/VolumeSnapshotBulkDelete.tsx index f3e3951c4..5cbc1b3ea 100644 --- a/src/pages/storage/actions/snapshots/VolumeSnapshotBulkDelete.tsx +++ b/src/pages/storage/actions/snapshots/VolumeSnapshotBulkDelete.tsx @@ -1,9 +1,13 @@ -import React, { FC, ReactNode, useState } from "react"; +import React, { FC, useState } from "react"; import { deleteVolumeSnapshotBulk } from "api/volume-snapshots"; import { useQueryClient } from "@tanstack/react-query"; import { queryKeys } from "util/queryKeys"; import { pluralizeSnapshot } from "util/instanceBulkActions"; -import { ConfirmationButton, Icon } from "@canonical/react-components"; +import { + ConfirmationButton, + Icon, + useNotify, +} from "@canonical/react-components"; import classnames from "classnames"; import { useEventQueue } from "context/eventQueue"; import { getPromiseSettledCounts } from "util/helpers"; @@ -14,8 +18,6 @@ interface Props { snapshotNames: string[]; onStart: () => void; onFinish: () => void; - onSuccess: (message: string) => void; - onFailure: (title: string, e: unknown, message?: ReactNode) => void; } const VolumeSnapshotBulkDelete: FC = ({ @@ -23,10 +25,9 @@ const VolumeSnapshotBulkDelete: FC = ({ snapshotNames, onStart, onFinish, - onSuccess, - onFailure, }) => { const eventQueue = useEventQueue(); + const notify = useNotify(); const [isLoading, setLoading] = useState(false); const queryClient = useQueryClient(); @@ -40,13 +41,13 @@ const VolumeSnapshotBulkDelete: FC = ({ const { fulfilledCount, rejectedCount } = getPromiseSettledCounts(results); if (fulfilledCount === count) { - onSuccess( + notify.success( `${snapshotNames.length} ${pluralizeSnapshot( snapshotNames.length, )} deleted`, ); } else if (rejectedCount === count) { - onFailure( + notify.failure( "Snapshot bulk deletion failed", undefined, <> @@ -54,7 +55,7 @@ const VolumeSnapshotBulkDelete: FC = ({ , ); } else { - onFailure( + notify.failure( "Snapshot bulk deletion partially failed", undefined, <> diff --git a/src/pages/storage/forms/CreateVolumeSnapshotForm.tsx b/src/pages/storage/forms/CreateVolumeSnapshotForm.tsx index 840b0e15f..b7c97423b 100644 --- a/src/pages/storage/forms/CreateVolumeSnapshotForm.tsx +++ b/src/pages/storage/forms/CreateVolumeSnapshotForm.tsx @@ -53,7 +53,7 @@ const CreateVolumeSnapshotForm: FC = ({ close, volume }) => { query.queryKey[0] === queryKeys.volumes || query.queryKey[0] === queryKeys.storage, }); - notify.queue(notify.success(`Snapshot ${values.name} created.`)); + notify.success(`Snapshot ${values.name} created.`); close(); resetForm(); }, diff --git a/src/pages/storage/forms/EditVolumeSnapshotForm.tsx b/src/pages/storage/forms/EditVolumeSnapshotForm.tsx index 0e3b2709e..043691e4e 100644 --- a/src/pages/storage/forms/EditVolumeSnapshotForm.tsx +++ b/src/pages/storage/forms/EditVolumeSnapshotForm.tsx @@ -33,7 +33,7 @@ const EditVolumeSnapshotForm: FC = ({ volume, snapshot, close }) => { query.queryKey[0] === queryKeys.volumes || query.queryKey[0] === queryKeys.storage, }); - notify.queue(notify.success(`Snapshot ${name} saved.`)); + notify.success(`Snapshot ${name} saved.`); formik.setSubmitting(false); close(); }; diff --git a/yarn.lock b/yarn.lock index bc5f3cfbd..d7d021984 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2086,22 +2086,21 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@canonical/react-components@0.47.1": - version "0.47.1" - resolved "https://registry.yarnpkg.com/@canonical/react-components/-/react-components-0.47.1.tgz#4f870b47f688d06f0c9a018dc13c167479b3abd6" - integrity sha512-Uue9LyUQH5OLB8FcOccDpnlLYqxlXO3WT6ibKIs8oSxn+EUNI6xXKLljawnZGf7dtMiSdrv4ieaeYQmnV5mtlg== +"@canonical/react-components@0.47.3": + version "0.47.3" + resolved "https://registry.yarnpkg.com/@canonical/react-components/-/react-components-0.47.3.tgz#9937e14f44583e5662561b5bfe63d96fba5a7fe0" + integrity sha512-M58N593Qc+AsOfgmwU205VEvdu9zBIQEhTs2YgTeshfLMx5MICTh2NhKBYCsWjgRTANaagAJJgdTovKawzgWcQ== dependencies: "@types/jest" "27.5.2" - "@types/node" "18.18.5" - "@types/react" "18.2.28" - "@types/react-dom" "18.2.13" - "@types/react-table" "7.7.16" - classnames "2.3.2" - nanoid "3.3.6" + "@types/node" "18.19.4" + "@types/react" "18.2.46" + "@types/react-dom" "18.2.18" + "@types/react-table" "7.7.19" + classnames "2.5.1" + nanoid "3.3.7" prop-types "15.8.1" - react-router-dom "6.17.0" react-table "7.8.0" - react-useportal "1.0.18" + react-useportal "1.0.19" "@csstools/css-parser-algorithms@^2.4.0": version "2.5.0" @@ -2811,10 +2810,10 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45" integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw== -"@remix-run/router@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.10.0.tgz#e2170dc2049b06e65bbe883adad0e8ddf8291278" - integrity sha512-Lm+fYpMfZoEucJ7cMxgt4dYt8jLfbpwRCzAjm9UgSLOkmlqo9gupxt6YX3DY0Fk155NT9l17d/ydi+964uS9Lw== +"@remix-run/router@1.14.2": + version "1.14.2" + resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.14.2.tgz#4d58f59908d9197ba3179310077f25c88e49ed17" + integrity sha512-ACXpdMM9hmKZww21yEqWwiLws/UPLhNKvimN8RrYSqPSvB3ov7sLvAcfvaxePeLvccTQKGdkDIhLYApZVDFuKg== "@rollup/plugin-babel@^5.2.0": version "5.3.1" @@ -3245,10 +3244,12 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.3.tgz#f0b991c32cfc6a4e7f3399d6cb4b8cf9a0315014" integrity sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw== -"@types/node@18.18.5": - version "18.18.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.18.5.tgz#afc0fd975df946d6e1add5bbf98264225b212244" - integrity sha512-4slmbtwV59ZxitY4ixUZdy1uRLf9eSIvBWPQxNjhHYWEtn0FryfKpyS2cvADYXTayWdKEIsJengncrVvkI4I6A== +"@types/node@18.19.4": + version "18.19.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.4.tgz#89672e84f11a2c19543d694dac00ab8d7bc20ddb" + integrity sha512-xNzlUhzoHotIsnFoXmJB+yWmBvFZgKCI9TtPIEdYIMM1KWfwuY8zh7wvc1u1OAXlC7dlf6mZVx/s+Y5KfFz19A== + dependencies: + undici-types "~5.26.4" "@types/parse-json@^4.0.0": version "4.0.0" @@ -3288,13 +3289,6 @@ "@types/cytoscape" "*" "@types/react" "*" -"@types/react-dom@18.2.13": - version "18.2.13" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.13.tgz#89cd7f9ec8b28c8b6f0392b9591671fb4a9e96b7" - integrity sha512-eJIUv7rPP+EC45uNYp/ThhSpE16k22VJUknt5OLoH9tbXoi8bMhwLf5xRuWMywamNbWzhrSmU7IBJfPup1+3fw== - dependencies: - "@types/react" "*" - "@types/react-dom@18.2.18": version "18.2.18" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.18.tgz#16946e6cd43971256d874bc3d0a72074bb8571dd" @@ -3319,10 +3313,10 @@ "@types/history" "^4.7.11" "@types/react" "*" -"@types/react-table@7.7.16": - version "7.7.16" - resolved "https://registry.yarnpkg.com/@types/react-table/-/react-table-7.7.16.tgz#e143e2c0fa2dcac739fcc8055e58f5a7a9c0e5fb" - integrity sha512-khfVwkNkvFnQV+Dx5Z/4jeMWIi+qytR8/Hl89fMPQ3aGiIgVlnghwdnyrq45UVSU+9wTqQFL0kUmIk4MGaM20Q== +"@types/react-table@7.7.19": + version "7.7.19" + resolved "https://registry.yarnpkg.com/@types/react-table/-/react-table-7.7.19.tgz#5175cb52a7df9e0234a67bdbdfe592738c92c862" + integrity sha512-47jMa1Pai7ily6BXJCW33IL5ghqmCWs2VM9s+h1D4mCaK5P4uNkZOW3RMMg8MCXBvAJ0v9+sPqKjhid0PaJPQA== dependencies: "@types/react" "*" @@ -3335,10 +3329,10 @@ "@types/scheduler" "*" csstype "^3.0.2" -"@types/react@18.2.28": - version "18.2.28" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.28.tgz#86877465c0fcf751659a36c769ecedfcfacee332" - integrity sha512-ad4aa/RaaJS3hyGz0BGegdnSRXQBkd1CCYDCdNjBPg90UUpLgo+WlJqb9fMYUxtehmzF3PJaTWqRZjko6BRzBg== +"@types/react@18.2.46": + version "18.2.46" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.46.tgz#f04d6c528f8f136ea66333bc66abcae46e2680df" + integrity sha512-nNCvVBcZlvX4NU1nRRNV/mFl1nNRuTuslAJglQsq+8ldXe5Xv0Wd2f7WTE3jOxhLH2BFfiZGC6GCp+kHQbgG+w== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -4779,10 +4773,10 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== -classnames@2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924" - integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw== +classnames@2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" + integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== clean-css@^5.2.2: version "5.3.2" @@ -8828,21 +8822,16 @@ multicast-dns@^7.2.5: dns-packet "^5.2.2" thunky "^1.0.2" -nanoid@3.3.6: - version "3.3.6" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" - integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== +nanoid@3.3.7, nanoid@^3.3.7: + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== nanoid@^3.3.4: version "3.3.4" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== -nanoid@^3.3.7: - version "3.3.7" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" - integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== - natural-compare-lite@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" @@ -10297,20 +10286,20 @@ react-refresh@^0.14.0: resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e" integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ== -react-router-dom@6.17.0: - version "6.17.0" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.17.0.tgz#ea73f89186546c1cf72b10fcb7356d874321b2ad" - integrity sha512-qWHkkbXQX+6li0COUUPKAUkxjNNqPJuiBd27dVwQGDNsuFBdMbrS6UZ0CLYc4CsbdLYTckn4oB4tGDuPZpPhaQ== +react-router-dom@6.21.3: + version "6.21.3" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.21.3.tgz#ef3a7956a3699c7b82c21fcb3dbc63c313ed8c5d" + integrity sha512-kNzubk7n4YHSrErzjLK72j0B5i969GsuCGazRl3G6j1zqZBLjuSlYBdVdkDOgzGdPIffUOc9nmgiadTEVoq91g== dependencies: - "@remix-run/router" "1.10.0" - react-router "6.17.0" + "@remix-run/router" "1.14.2" + react-router "6.21.3" -react-router@6.17.0: - version "6.17.0" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.17.0.tgz#7b680c4cefbc425b57537eb9c73bedecbdc67c1e" - integrity sha512-YJR3OTJzi3zhqeJYADHANCGPUu9J+6fT5GLv82UWRGSxu6oJYCKVmxUcaBQuGm9udpWmPsvpme/CdHumqgsoaA== +react-router@6.21.3: + version "6.21.3" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.21.3.tgz#8086cea922c2bfebbb49c6594967418f1f167d70" + integrity sha512-a0H638ZXULv1OdkmiK6s6itNhoy33ywxmUFT/xtSoVyf9VnC7n7+VT4LjVzdIHSaF5TIh9ylUgxMXksHTgGrKg== dependencies: - "@remix-run/router" "1.10.0" + "@remix-run/router" "1.14.2" react-scripts@5.0.1: version "5.0.1" @@ -10372,13 +10361,6 @@ react-table@7.8.0: resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.8.0.tgz#07858c01c1718c09f7f1aed7034fcfd7bda907d2" integrity sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA== -react-useportal@1.0.18: - version "1.0.18" - resolved "https://registry.yarnpkg.com/react-useportal/-/react-useportal-1.0.18.tgz#b3baf14962f44402b2d0d6152acc2035c5342bd8" - integrity sha512-dGuT/yyE2T9RtUxRZJYkIX8tLHC7KxAxbMw/Ufjiwo8ixoDYzkk9LFKGnARtCtFz6Yd5AoP7fVymrN3eT04jiA== - dependencies: - use-ssr "^1.0.25" - react-useportal@1.0.19: version "1.0.19" resolved "https://registry.yarnpkg.com/react-useportal/-/react-useportal-1.0.19.tgz#473168f1a1c4009833d49a46b05d974a5850a166" @@ -11991,6 +11973,11 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc"