Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions libs/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -411,7 +407,7 @@ const MassResumeDevicesModalContent = ({ onClose }: MassResumeDevicesModalProps)
{resumedCount !== undefined && hasResumedAllExpected && (
<StackItem>
<Alert isInline variant="success" title={t('Resume successful')}>
{t('{{ resumedCount }} devices were resumed', { resumedCount })}
{t('{{ count }} devices were resumed', { count: resumedCount })}
</Alert>
</StackItem>
)}
Expand All @@ -429,12 +425,12 @@ const MassResumeDevicesModalContent = ({ onClose }: MassResumeDevicesModalProps)
</Stack>
{showResumeAllConfirmation && (
<ResumeAllDevicesConfirmationDialog
deviceCountNum={deviceCountNum}
onClose={(doConfirm) => {
devicesToResume={deviceCountNum}
onClose={(resumedCount) => {
setResumedCount(resumedCount);
setIsCountLoading(false);
setCountError(null);
setShowResumeAllConfirmation(false);
if (doConfirm) {
performResume();
}
}}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -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 (
<Stack hasGutter>
<StackItem>
<TextContent>
<Text>
<Trans t={t} count={devicesToResume}>
You are about to resume all <strong>{deviceCount}</strong> suspended devices.
</Trans>
</Text>
</TextContent>
<TextContent>
<Text>
{t(
'This action is irreversible and will allow all affected devices to receive new configuration updates from the server.',
)}
</Text>
</TextContent>
</StackItem>
</Stack>
);
};

const ModalContentAfterResume = ({
hasResumedAll,
resumedCount,
submitError,
}: {
hasResumedAll: boolean;
resumedCount: number;
submitError: string | undefined;
}) => {
const { t } = useTranslation();
if (submitError) {
return (
<Alert isInline variant="danger" title={t('Resume devices failed')}>
{submitError}
</Alert>
);
}

const alertType = hasResumedAll ? 'success' : 'warning';
const title = hasResumedAll ? t('Resume successful') : t('Resumed with warnings');
return (
<Modal
variant={ModalVariant.small}
title={t('Resume all {{ deviceCount }} devices?', { deviceCount })}
isOpen
onClose={() => onClose(false)}
actions={[
<Button key="confirm" variant="primary" onClick={() => onClose(true)}>
<Alert isInline variant={alertType} title={title}>
{t('{{ count }} devices were resumed', { count: resumedCount })}
</Alert>
);
};

const ResumeAllDevicesConfirmationModal = ({ devicesToResume, onClose }: ResumeAllDevicesConfirmationModalProps) => {
const { t } = useTranslation();
const { post } = useFetch();

const [resumedCount, setResumedCount] = React.useState<number | undefined>(undefined);
const [isSubmitting, setIsSubmitting] = React.useState(false);
const [submitError, setSubmitError] = React.useState<string | undefined>(undefined);

const isBeforeResume = resumedCount === undefined;
const title = isBeforeResume
? t('Resume all {{ count }} devices?', { count: devicesToResume })
: t('Resume devices result');

const buttons = isBeforeResume
? [
<Button
key="confirm"
variant="primary"
isDisabled={isSubmitting}
onClick={async () => {
setIsSubmitting(true);
try {
const resumeRequest: DeviceResumeRequest = {
labelSelector: '', // All devices
};
const resumeResponse = await post<DeviceResumeRequest, DeviceResumeResponse>(
'deviceactions/resume',
resumeRequest,
);
setResumedCount(resumeResponse.resumedDevices || 0);
} catch (error) {
setResumedCount(0);
setSubmitError(getErrorMessage(error));
} finally {
setIsSubmitting(false);
}
}}
>
{t('Resume all devices')}
</Button>,
<Button key="cancel" variant="link" onClick={() => onClose(false)}>
<Button key="cancel" variant="link" isDisabled={isSubmitting} onClick={() => onClose(undefined)}>
{t('Cancel')}
</Button>,
]}
>
<Stack hasGutter>
<StackItem>
<TextContent>
<Text>
<Trans t={t} count={deviceCountNum}>
You are about to resume all <strong>{deviceCount}</strong> suspended devices.
</Trans>
</Text>
</TextContent>
<TextContent>
<Text>
{t(
'This action is irreversible and will allow all affected devices to receive new configuration updates from the server.',
)}
</Text>
</TextContent>
</StackItem>
</Stack>
]
: [
<Button key="close" variant="primary" onClick={() => onClose(resumedCount)}>
{t('Close')}
</Button>,
];
Comment thread
celdrake marked this conversation as resolved.

return (
<Modal variant={ModalVariant.small} title={title} isOpen onClose={() => onClose(undefined)} actions={buttons}>
{isBeforeResume ? (
<ModalContentBeforeResume devicesToResume={devicesToResume} />
) : (
<ModalContentAfterResume
resumedCount={resumedCount}
hasResumedAll={resumedCount === devicesToResume}
submitError={submitError}
/>
)}
</Modal>
);
};
Expand Down