Skip to content
Open
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
23 changes: 13 additions & 10 deletions libs/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -969,12 +969,14 @@
"This device uses package-based OS management. System image configuration is not available for this device.": "This device uses package-based OS management. System image configuration is not available for this device.",
"System image is managed by Software Catalog": "System image is managed by Software Catalog",
"Must be a reference to a bootable container image (such as \"quay.io/<my-org>/my-rhel-with-fc-agent:<version>\"). If you do not want to manage your OS from Edge management, leave this field empty.": "Must be a reference to a bootable container image (such as \"quay.io/<my-org>/my-rhel-with-fc-agent:<version>\"). If you do not want to manage your OS from Edge management, leave this field empty.",
"Use basic configurations": "Use basic configurations",
"Advanced configurations": "Advanced configurations",
"Update policies": "Update policies",
"Update policies allow you to control when updates should be downloaded and applied.": "Update policies allow you to control when updates should be downloaded and applied.",
"Default update policy": "Default update policy",
"The device will download and apply updates as soon as they are available.": "The device will download and apply updates as soon as they are available.",
"Update behavior": "Update behavior",
"Immediate updates": "Immediate updates",
"Devices receive updates as soon as they are available.": "Devices receive updates as soon as they are available.",
"Customize update scheduling": "Customize update scheduling",
"Set maintenance windows to control when updates are downloaded and applied.": "Set maintenance windows to control when updates are downloaded and applied.",
"Customize updates": "Customize updates",
"Maintenance windows": "Maintenance windows",
"Maintenance windows control when devices may download and install updates.": "Maintenance windows control when devices may download and install updates.",
"Device alias": "Device alias",
"Device labels": "Device labels",
"Unnamed": "Unnamed",
Expand Down Expand Up @@ -1100,12 +1102,13 @@
"Device selector": "Device selector",
"Rollout policy": "Rollout policy",
"Disruption budget": "Disruption budget",
"Set rollout policies": "Set rollout policies",
"Rollout policies allow you to control the order of updates for the fleet devices.": "Rollout policies allow you to control the order of updates for the fleet devices.",
"Customize rollout and scheduling": "Customize rollout and scheduling",
"Set rollout order, disruption budget, or maintenance windows.": "Set rollout order, disruption budget, or maintenance windows.",
"Set rollout order": "Set rollout order",
"Rollout order controls which fleet devices are updated and in what sequence.": "Rollout order controls which fleet devices are updated and in what sequence.",
"Set disruption budget": "Set disruption budget",
"Disruption budget allows you to limit the number of similar devices that may be updating simultaneously.": "Disruption budget allows you to limit the number of similar devices that may be updating simultaneously.",
"Set update policies": "Set update policies",
"All the devices that are part of this fleet will receive updates as soon as they are available.": "All the devices that are part of this fleet will receive updates as soon as they are available.",
"Set maintenance windows": "Set maintenance windows",
"Group devices by label keys": "Group devices by label keys",
"The disruption budget will be applied to all group combinations independently.": "The disruption budget will be applied to all group combinations independently.",
"Add label key": "Add label key",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ const EditDeviceWizard = () => {
...updatePolicyValues,
isAdvanced: true,
},
useBasicUpdateConfig: !updatePolicyValues.isAdvanced,
customizeUpdates: updatePolicyValues.isAdvanced,
}}
validationSchema={getValidationSchema(t)}
validateOnMount
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { Alert, Title } from '@patternfly/react-core';
import { FormGroup, Stack, StackItem, Title } from '@patternfly/react-core';
import { type FormikErrors, useFormikContext } from 'formik';

import { useTranslation } from '../../../../hooks/useTranslation';
Expand All @@ -8,7 +8,7 @@ import UpdateStepUpdatePolicy from '../../../Fleet/CreateFleet/steps/UpdateStepU
import FlightCtlForm from '../../../form/FlightCtlForm';
import { type DeviceSpecConfigFormValues } from '../../../../types/deviceSpec';
import { FormGroupWithHelperText } from '../../../common/WithHelperText';
import CheckboxField from '../../../form/CheckboxField';
import RadioField from '../../../form/RadioField';

export const deviceUpdatePolicyStepId = 'update-policy';

Expand All @@ -18,27 +18,46 @@ const UpdatePolicyStep = ({ isReadOnly }: { isReadOnly?: boolean }) => {
const { t } = useTranslation();

const {
values: { useBasicUpdateConfig },
values: { customizeUpdates },
} = useFormikContext<DeviceSpecConfigFormValues>();

return (
<FlightCtlForm>
<CheckboxField name="useBasicUpdateConfig" label={t('Use basic configurations')} isDisabled={isReadOnly} />
{!useBasicUpdateConfig ? (
<>
<Title headingLevel="h3">{t('Advanced configurations')}</Title>
<FormGroupWithHelperText
label={t('Update policies')}
content={t('Update policies allow you to control when updates should be downloaded and applied.')}
>
<UpdateStepUpdatePolicy isReadOnly={isReadOnly} />
</FormGroupWithHelperText>
</>
) : (
<Alert isInline variant="info" title={t('Default update policy')}>
{t('The device will download and apply updates as soon as they are available.')}
</Alert>
)}
<Stack hasGutter>
<StackItem>
<FormGroup label={t('Update behavior')} role="radiogroup">
<Stack hasGutter>
<RadioField
id="device-update-immediate"
name="customizeUpdates"
label={t('Immediate updates')}
description={t('Devices receive updates as soon as they are available.')}
checkedValue={false}
isDisabled={isReadOnly}
/>
<RadioField
id="device-update-customize"
name="customizeUpdates"
label={t('Customize update scheduling')}
description={t('Set maintenance windows to control when updates are downloaded and applied.')}
checkedValue={true}
isDisabled={isReadOnly}
/>
</Stack>
</FormGroup>
</StackItem>
{customizeUpdates && (
<StackItem>
<Title headingLevel="h3">{t('Customize updates')}</Title>
<FormGroupWithHelperText
label={t('Maintenance windows')}
content={t('Maintenance windows control when devices may download and install updates.')}
>
<UpdateStepUpdatePolicy isReadOnly={isReadOnly} />
</FormGroupWithHelperText>
</StackItem>
)}
</Stack>
</FlightCtlForm>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const getValidationSchema = (t: TFunction) =>
configTemplates: validConfigTemplatesSchema(t),
applications: validApplicationsSchema(t),
updatePolicy:
!values.useBasicUpdateConfig && values.updatePolicy.isAdvanced ? validUpdatePolicySchema(t) : Yup.object(),
values.customizeUpdates && values.updatePolicy.isAdvanced ? validUpdatePolicySchema(t) : Yup.object(),
}),
);

Expand Down Expand Up @@ -78,7 +78,7 @@ export const getDevicePatches = (currentDevice: Device, updatedDevice: EditDevic
// Updates
const updatesPatches = getUpdatePolicyPatches('/spec/updatePolicy', currentDevice.spec?.updatePolicy, {
...updatedDevice.updatePolicy,
isAdvanced: !updatedDevice.useBasicUpdateConfig,
isAdvanced: updatedDevice.customizeUpdates,
} as Required<UpdatePolicyForm>);
allPatches = allPatches.concat(updatesPatches);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ const ReviewStep = ({ error }: { error?: unknown }) => {
</DescriptionListDescription>
</DescriptionListGroup>
)}
{!values.useBasicUpdateConfig && (
{values.customizeUpdates && (
<>
{values.rolloutPolicy.isAdvanced && (
<DescriptionListGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { Alert, FormSection } from '@patternfly/react-core';
import { FormGroup, FormSection, Stack, StackItem } from '@patternfly/react-core';
import { type FormikErrors, useFormikContext } from 'formik';

import { useTranslation } from '../../../../hooks/useTranslation';
Expand All @@ -11,6 +11,7 @@ import UpdateStepRolloutPolicy from './UpdateStepRolloutPolicy';
import UpdateStepDisruptionBudget from './UpdateStepDisruptionBudget';
import UpdateStepUpdatePolicy from './UpdateStepUpdatePolicy';
import CheckboxField from '../../../form/CheckboxField';
import RadioField from '../../../form/RadioField';

import './UpdatePolicyStep.css';

Expand All @@ -23,60 +24,81 @@ const UpdatePolicyStep = ({ isReadOnly }: { isReadOnly: boolean }) => {
const { t } = useTranslation();

const {
values: { useBasicUpdateConfig, rolloutPolicy, disruptionBudget, updatePolicy },
values: { customizeUpdates, rolloutPolicy, disruptionBudget, updatePolicy },
} = useFormikContext<FleetFormValues>();

return (
<FlightCtlForm>
<CheckboxField name="useBasicUpdateConfig" label={t('Use basic configurations')} isDisabled={isReadOnly} />
{!useBasicUpdateConfig ? (
<FormSection title={t('Advanced configurations')} titleElement="h1" className="fctl-update-policy--advanced">
{/* Rollout policies */}
<CheckboxField
name="rolloutPolicy.isAdvanced"
label={
<LabelWithHelperText
label={t('Set rollout policies')}
content={t('Rollout policies allow you to control the order of updates for the fleet devices.')}
<Stack hasGutter>
<StackItem>
<FormGroup label={t('Update behavior')} role="radiogroup">
<Stack hasGutter>
<RadioField
id="fleet-update-immediate"
name="customizeUpdates"
label={t('Immediate updates')}
description={t('Devices receive updates as soon as they are available.')}
checkedValue={false}
isDisabled={isReadOnly}
/>
<RadioField
id="fleet-update-customize"
name="customizeUpdates"
label={t('Customize rollout and scheduling')}
description={t('Set rollout order, disruption budget, or maintenance windows.')}
checkedValue={true}
isDisabled={isReadOnly}
/>
</Stack>
</FormGroup>
</StackItem>
{customizeUpdates && (
<StackItem>
<FormSection title={t('Customize updates')} titleElement="h1" className="fctl-update-policy--advanced">
{/* Rollout order */}
<CheckboxField
name="rolloutPolicy.isAdvanced"
label={
<LabelWithHelperText
label={t('Set rollout order')}
content={t('Rollout order controls which fleet devices are updated and in what sequence.')}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
content={t('Rollout order controls which fleet devices are updated and in what sequence.')}
content={t('Rollout order controls in what sequence fleet devices are updated.')}

The proposed wording is not accurate.

Eventually all the devices in the fleet will receive the updates.
Through the batches, users can control the order in which devices update. Any device that doesn't match any defined "batch", is bundled together as a last "default batch" and those devices receive the updates last.

(See the note with title "Batch sequencing":)

Batches will be applied from first to last.
Devices that are not part of any batch will be updated last.

/>
}
isDisabled={isReadOnly}
body={rolloutPolicy.isAdvanced && <UpdateStepRolloutPolicy isReadOnly={isReadOnly} />}
/>
}
isDisabled={isReadOnly}
body={rolloutPolicy.isAdvanced && <UpdateStepRolloutPolicy isReadOnly={isReadOnly} />}
/>

{/* Disruption budget */}
<CheckboxField
name="disruptionBudget.isAdvanced"
label={
<LabelWithHelperText
label={t('Set disruption budget')}
content={t(
'Disruption budget allows you to limit the number of similar devices that may be updating simultaneously.',
)}
{/* Disruption budget */}
<CheckboxField
name="disruptionBudget.isAdvanced"
label={
<LabelWithHelperText
label={t('Set disruption budget')}
content={t(
'Disruption budget allows you to limit the number of similar devices that may be updating simultaneously.',
)}
/>
}
isDisabled={isReadOnly}
body={disruptionBudget.isAdvanced && <UpdateStepDisruptionBudget isReadOnly={isReadOnly} />}
/>
}
isDisabled={isReadOnly}
body={disruptionBudget.isAdvanced && <UpdateStepDisruptionBudget isReadOnly={isReadOnly} />}
/>

{/* Update (and download) policies */}
<CheckboxField
name="updatePolicy.isAdvanced"
label={
<LabelWithHelperText
label={t('Set update policies')}
content={t('Update policies allow you to control when updates should be downloaded and applied.')}
{/* Maintenance windows */}
<CheckboxField
name="updatePolicy.isAdvanced"
label={
<LabelWithHelperText
label={t('Set maintenance windows')}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something for UX to consider.

"Maintenance windows" is not a concept that users would find in our documentation etc, For a UI user looking to expand the information on some of these concepts, using different wording could be confusing.

I'll raise this as a separate team discussion.

content={t('Maintenance windows control when devices may download and install updates.')}
/>
}
isDisabled={isReadOnly}
body={updatePolicy.isAdvanced && <UpdateStepUpdatePolicy isReadOnly={isReadOnly} />}
/>
}
isDisabled={isReadOnly}
body={updatePolicy.isAdvanced && <UpdateStepUpdatePolicy isReadOnly={isReadOnly} />}
/>
</FormSection>
) : (
<Alert isInline variant="info" title={t('Default update policy')}>
{t('All the devices that are part of this fleet will receive updates as soon as they are available.')}
</Alert>
)}
</FormSection>
</StackItem>
)}
</Stack>
</FlightCtlForm>
);
};
Expand Down
16 changes: 7 additions & 9 deletions libs/ui-components/src/components/Fleet/CreateFleet/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,13 @@ export const getValidationSchema = (t: TFunction) => {
applications: validApplicationsSchema(t),
systemdUnits: systemdUnitListValidationSchema(t),
rolloutPolicy:
!values.useBasicUpdateConfig && values.rolloutPolicy?.isAdvanced
? validFleetRolloutPolicySchema(t)
: Yup.object(),
values.customizeUpdates && values.rolloutPolicy?.isAdvanced ? validFleetRolloutPolicySchema(t) : Yup.object(),
disruptionBudget:
!values.useBasicUpdateConfig && values.disruptionBudget?.isAdvanced
values.customizeUpdates && values.disruptionBudget?.isAdvanced
? validFleetDisruptionBudgetSchema(t)
: Yup.object(),
updatePolicy:
!values.useBasicUpdateConfig && values.updatePolicy?.isAdvanced ? validUpdatePolicySchema(t) : Yup.object(),
values.customizeUpdates && values.updatePolicy?.isAdvanced ? validUpdatePolicySchema(t) : Yup.object(),
}),
);
};
Expand Down Expand Up @@ -142,7 +140,7 @@ export const getFleetPatches = (currentFleet: Fleet, updatedFleet: FleetFormValu
currentFleet.spec.template.spec.updatePolicy,
{
...updatedFleet.updatePolicy,
isAdvanced: !updatedFleet.useBasicUpdateConfig && updatedFleet.updatePolicy.isAdvanced,
isAdvanced: updatedFleet.customizeUpdates && updatedFleet.updatePolicy.isAdvanced,
} as Required<UpdatePolicyForm>,
);
allPatches = allPatches.concat(updatePolicyPatches);
Expand Down Expand Up @@ -190,7 +188,7 @@ export const getFleetResource = (values: FleetFormValues): Fleet => {
if (values.registerMicroShift) {
fleet.spec.template.spec.config?.push(ACMCrdConfig, ACMImportConfig, MicroshiftRegistrationHook);
}
if (!values.useBasicUpdateConfig) {
if (values.customizeUpdates) {
if (values.rolloutPolicy.isAdvanced || values.disruptionBudget.isAdvanced) {
fleet.spec.rolloutPolicy = getRolloutPolicyData(values);
}
Expand Down Expand Up @@ -225,7 +223,7 @@ export const getInitialValues = (fleet?: Fleet): FleetFormValues => {
rolloutPolicy,
disruptionBudget,
updatePolicy,
useBasicUpdateConfig: !rolloutPolicy.isAdvanced && !disruptionBudget.isAdvanced && !updatePolicy.isAdvanced,
customizeUpdates: rolloutPolicy.isAdvanced || disruptionBudget.isAdvanced || updatePolicy.isAdvanced,
};
}

Expand All @@ -241,6 +239,6 @@ export const getInitialValues = (fleet?: Fleet): FleetFormValues => {
rolloutPolicy: getRolloutPolicyValues(undefined),
disruptionBudget: getDisruptionBudgetValues(undefined),
updatePolicy: getUpdatePolicyValues(undefined),
useBasicUpdateConfig: true,
customizeUpdates: false,
};
};
9 changes: 6 additions & 3 deletions libs/ui-components/src/components/form/RadioField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ export interface RadioFieldProps extends Omit<RadioProps, 'onChange' | 'ref' | '
checkedValue?: unknown;
name: string;
helperText?: React.ReactNode;
description?: React.ReactNode;
onChangeCustom?: (checkedValue: unknown) => void;
noDefaultOnChange?: boolean;
}

const RadioField = ({
helperText,
description,
checkedValue,
name,
onChangeCustom,
Expand All @@ -31,7 +33,7 @@ const RadioField = ({
if (noDefaultOnChange) {
return;
}
await setValue(checkedValue || true, true);
await setValue(checkedValue !== undefined ? checkedValue : true, true);
await setTouched(true);
}
};
Expand All @@ -44,11 +46,12 @@ const RadioField = ({
{...field}
{...props}
id={fieldId}
description={description}
onChange={onChange}
isChecked={checkedValue ? field.value === checkedValue : !!field.value}
isChecked={checkedValue !== undefined ? field.value === checkedValue : !!field.value}
/>

<DefaultHelperText helperText={helperText} />
<DefaultHelperText helperText={description ? undefined : helperText} />

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Currently, none of the "RadioField" usages in the UI use the helperText.

The description field has IMO a nicer UX, so we could drop helperText and keep only description.

I can do this as a follow-up in any case.

<ErrorHelperText meta={meta} />
</FormGroup>
);
Expand Down
2 changes: 1 addition & 1 deletion libs/ui-components/src/types/deviceSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ export type DeviceSpecConfigFormValues = {
systemdUnits: SystemdUnitFormValue[];
updatePolicy: UpdatePolicyForm;
registerMicroShift: boolean;
useBasicUpdateConfig: boolean;
customizeUpdates: boolean;
};

export type EditDeviceFormValues = DeviceSpecConfigFormValues & {
Expand Down
Loading
Loading