diff --git a/libs/i18n/locales/en/translation.json b/libs/i18n/locales/en/translation.json
index 95ca8fa2f..062c9fe10 100644
--- a/libs/i18n/locales/en/translation.json
+++ b/libs/i18n/locales/en/translation.json
@@ -659,12 +659,15 @@
"No suspended devices match the specified labels.": "No suspended devices match the specified labels.",
"Resume devices failed": "Resume devices failed",
"Resume successful": "Resume successful",
- "{{ resumedCount }} devices were resumed": "{{ resumedCount }} devices were resumed",
+ "{{ count }} devices were resumed_one": "{{ count }} device was resumed",
+ "{{ count }} devices were resumed_other": "{{ count }} devices were resumed",
"Resumed with warnings": "Resumed with warnings",
"{{ expectedCount }} devices to resume, and {{ resumedCount }} resumed successfully": "{{ expectedCount }} devices to resume, and {{ resumedCount }} resumed successfully",
- "Resume all {{ deviceCount }} devices?": "Resume all {{ deviceCount }} devices?",
+ "Resume all {{ count }} devices?_one": "Resume {{ count }} device?",
+ "Resume all {{ count }} devices?_other": "Resume all {{ count }} devices?",
+ "Resume devices result": "Resume devices result",
"Resume all devices": "Resume all devices",
- "Resume devices?_one": "Resume devices?",
+ "Resume devices?_one": "Resume device?",
"Resume devices?_other": "Resume devices?",
"Resume": "Resume",
"This action will resolve the configuration conflict and allow the devices to receive new updates from the server. This action is irreversible, please ensure the devices' assigned configuration is correct before proceeding._one": "This action will resolve the configuration conflict and allow the device to receive new updates from the server. This action is irreversible, please ensure the device's assigned configuration is correct before proceeding.",
diff --git a/libs/ui-components/src/components/modals/massModals/ResumeDevicesModal/MassResumeDevicesModal.tsx b/libs/ui-components/src/components/modals/massModals/ResumeDevicesModal/MassResumeDevicesModal.tsx
index 6f1ca724b..fd9687335 100644
--- a/libs/ui-components/src/components/modals/massModals/ResumeDevicesModal/MassResumeDevicesModal.tsx
+++ b/libs/ui-components/src/components/modals/massModals/ResumeDevicesModal/MassResumeDevicesModal.tsx
@@ -130,22 +130,18 @@ const MassResumeDevicesModalContent = ({ onClose }: MassResumeDevicesModalProps)
setSubmitError(undefined);
try {
- let labels: FlightCtlLabel[];
+ let labels: FlightCtlLabel[] = [];
- if (values.mode === SelectionMode.ALL) {
- labels = [];
- } else {
- if (values.mode === SelectionMode.FLEET) {
- labels = getSelectedFleetLabels(fleets, values.fleetId);
- } else {
- labels = values.labels;
- }
- // This shouldn't happen due to validations, but since an empty label selector would target all existing devices,
- // wake sure the UI does't accidentally submit a request with an empty selector
- const emptySelection = labels.every((label) => label.key === '');
- if (labels.length === 0 || emptySelection) {
- throw new Error('The current selection would target all devices.');
- }
+ if (values.mode === SelectionMode.FLEET) {
+ labels = getSelectedFleetLabels(fleets, values.fleetId);
+ } else if (values.mode === SelectionMode.LABELS) {
+ labels = values.labels;
+ }
+ // This shouldn't happen due to validations, but since an empty label selector would target all existing devices,
+ // wake sure the UI does't accidentally submit a request with an empty selector
+ const emptySelection = labels.every((label) => label.key === '');
+ if (labels.length === 0 || emptySelection) {
+ throw new Error('The current selection would target all devices.');
}
const resumeRequest: DeviceResumeRequest = {
@@ -411,7 +407,7 @@ const MassResumeDevicesModalContent = ({ onClose }: MassResumeDevicesModalProps)
{resumedCount !== undefined && hasResumedAllExpected && (
- {t('{{ resumedCount }} devices were resumed', { resumedCount })}
+ {t('{{ count }} devices were resumed', { count: resumedCount })}
)}
@@ -429,12 +425,12 @@ const MassResumeDevicesModalContent = ({ onClose }: MassResumeDevicesModalProps)
{showResumeAllConfirmation && (
{
+ devicesToResume={deviceCountNum}
+ onClose={(resumedCount) => {
+ setResumedCount(resumedCount);
+ setIsCountLoading(false);
+ setCountError(null);
setShowResumeAllConfirmation(false);
- if (doConfirm) {
- performResume();
- }
}}
/>
)}
diff --git a/libs/ui-components/src/components/modals/massModals/ResumeDevicesModal/ResumeAllDevicesConfirmationDialog.tsx b/libs/ui-components/src/components/modals/massModals/ResumeDevicesModal/ResumeAllDevicesConfirmationDialog.tsx
index e71130372..a61b91eb0 100644
--- a/libs/ui-components/src/components/modals/massModals/ResumeDevicesModal/ResumeAllDevicesConfirmationDialog.tsx
+++ b/libs/ui-components/src/components/modals/massModals/ResumeDevicesModal/ResumeAllDevicesConfirmationDialog.tsx
@@ -1,53 +1,132 @@
import * as React from 'react';
-import { Button, Modal, ModalVariant, Stack, StackItem, Text, TextContent } from '@patternfly/react-core';
import { Trans } from 'react-i18next';
+import { Alert, Button, Modal, ModalVariant, Stack, StackItem, Text, TextContent } from '@patternfly/react-core';
+import { DeviceResumeRequest, DeviceResumeResponse } from '@flightctl/types';
import { useTranslation } from '../../../../hooks/useTranslation';
+import { useFetch } from '../../../../hooks/useFetch';
+import { getErrorMessage } from '../../../../utils/error';
interface ResumeAllDevicesConfirmationModalProps {
- deviceCountNum: number;
- onClose: (doConfirm: boolean) => void;
+ devicesToResume: number;
+ onClose: (resumedCount: number | undefined) => void;
isSubmitting?: boolean;
}
-const ResumeAllDevicesConfirmationModal = ({ deviceCountNum, onClose }: ResumeAllDevicesConfirmationModalProps) => {
+const ModalContentBeforeResume = ({ devicesToResume }: { devicesToResume: number }) => {
const { t } = useTranslation();
- const deviceCount = deviceCountNum.toString();
+ const deviceCount = devicesToResume.toString();
+ return (
+
+
+
+
+
+ You are about to resume all {deviceCount} suspended devices.
+
+
+
+
+
+ {t(
+ 'This action is irreversible and will allow all affected devices to receive new configuration updates from the server.',
+ )}
+
+
+
+
+ );
+};
+
+const ModalContentAfterResume = ({
+ hasResumedAll,
+ resumedCount,
+ submitError,
+}: {
+ hasResumedAll: boolean;
+ resumedCount: number;
+ submitError: string | undefined;
+}) => {
+ const { t } = useTranslation();
+ if (submitError) {
+ return (
+
+ {submitError}
+
+ );
+ }
+ const alertType = hasResumedAll ? 'success' : 'warning';
+ const title = hasResumedAll ? t('Resume successful') : t('Resumed with warnings');
return (
- onClose(false)}
- actions={[
-