Wizard: "Always blue" update to the Registration step

This updates the Registration step as per recent mocks.

There is now only one checkbox which switches between (register everything now / register later). When the checkbox is not checked "register later" option gets selected, activation key dropdown gets disabled and information about activation keys is hidden to support the fact that no activation key will be used.

When the checkbox is checked it either loads previously used activation key in the case of "on edit" scenario, looks for a recently used activation key in localStorage or in a case there are no activationKeys automatically creates and selects one.

The Next button gets disabled only when "register-now" is selected, but there is no activationKey selected. As the option to clear the dropdown got removed this should occur only when fetching/creating activation key.
This commit is contained in:
regexowl 2024-07-31 09:21:12 +02:00 committed by Ondřej Ezr
parent c4bbd413b3
commit 2caddb66c7
5 changed files with 96 additions and 223 deletions

View file

@ -48,7 +48,6 @@ import {
changeDistribution,
changeArchitecture,
initializeWizard,
selectActivationKey,
selectAwsAccountId,
selectAwsShareMethod,
selectAwsSourceId,
@ -60,10 +59,11 @@ import {
selectGcpEmail,
selectGcpShareMethod,
selectImageTypes,
selectRegistrationType,
addImageType,
selectSnapshotDate,
selectUseLatest,
selectRegistrationType,
selectActivationKey,
} from '../../store/wizardSlice';
import { resolveRelPath } from '../../Utilities/path';
import { ImageBuilderHeader } from '../sharedComponents/ImageBuilderHeader';
@ -177,17 +177,19 @@ const CreateImageWizard = ({ isEdit }: CreateImageWizardProps) => {
const azureSubscriptionId = useAppSelector(selectAzureSubscriptionId);
const azureResourceGroup = useAppSelector(selectAzureResourceGroup);
const azureSource = useAppSelector(selectAzureSource);
// Registration
const registrationType = useAppSelector(selectRegistrationType);
const activationKey = useAppSelector(selectActivationKey);
// Snapshots
const snapshotDate = useAppSelector(selectSnapshotDate);
const useLatest = useAppSelector(selectUseLatest);
const snapshotStepRequiresChoice = !useLatest && !snapshotDate;
// Filesystem
const [filesystemPristine, setFilesystemPristine] = useState(true);
const fileSystemValidation = useFilesystemValidation();
// Firstboot
const firstBootValidation = useFirstBootValidation();
// Details
const detailsValidation = useDetailsValidation();
let startIndex = 1; // default index
@ -326,7 +328,7 @@ const CreateImageWizard = ({ isEdit }: CreateImageWizardProps) => {
footer={
<CustomWizardFooter
disableNext={
registrationType !== 'register-later' && !activationKey
registrationType === 'register-now' && !activationKey
}
/>
}

View file

@ -4,13 +4,7 @@ import {
Alert,
FormGroup,
Spinner,
EmptyState,
Button,
EmptyStateIcon,
EmptyStateBody,
EmptyStateHeader,
EmptyStateFooter,
EmptyStateActions,
Text,
TextContent,
Popover,
@ -20,7 +14,6 @@ import {
SelectOption,
SelectVariant,
} from '@patternfly/react-core/deprecated';
import { WrenchIcon, AddCircleOIcon } from '@patternfly/react-icons';
import { ExternalLinkAltIcon, HelpIcon } from '@patternfly/react-icons';
import { useChrome } from '@redhat-cloud-services/frontend-components/useChrome';
import { addNotification } from '@redhat-cloud-services/frontend-components-notifications/redux';
@ -41,6 +34,7 @@ import {
changeBaseUrl,
changeServerUrl,
selectActivationKey,
selectRegistrationType,
} from '../../../../store/wizardSlice';
import { useGetEnvironment } from '../../../../Utilities/useGetEnvironment';
@ -89,41 +83,6 @@ export const PopoverActivation = () => {
);
};
type EmptyActivationsKeyStateProps = {
handleActivationKeyFn: () => void;
isLoading: boolean;
};
const EmptyActivationsKeyState = ({
handleActivationKeyFn,
isLoading,
}: EmptyActivationsKeyStateProps) => (
<EmptyState variant="xs">
<EmptyStateHeader
titleText="No activation keys found"
headingLevel="h4"
icon={<EmptyStateIcon icon={WrenchIcon} />}
/>
<EmptyStateBody>
Get started by building a default key, which will be generated and present
for you.
</EmptyStateBody>
<EmptyStateFooter>
<EmptyStateActions>
<Button
onClick={() => handleActivationKeyFn()}
icon={<AddCircleOIcon />}
isLoading={isLoading}
iconPosition="left"
variant="link"
>
Create activation key
</Button>
</EmptyStateActions>
</EmptyStateFooter>
</EmptyState>
);
const ManageKeysButton = () => {
const { isProd } = useGetEnvironment();
return (
@ -136,7 +95,7 @@ const ManageKeysButton = () => {
isInline
href={isProd() ? ACTIVATION_KEYS_PROD_URL : ACTIVATION_KEYS_STAGE_URL}
>
Activation keys page
Customer portal
</Button>
);
};
@ -145,6 +104,9 @@ const ActivationKeysList = () => {
const dispatch = useAppDispatch();
const activationKey = useAppSelector(selectActivationKey);
const registrationType = useAppSelector(selectRegistrationType);
const defaultActivationKeyName = 'activation-key-default';
const { isProd } = useGetEnvironment();
const [isOpen, setIsOpen] = useState(false);
@ -160,6 +122,10 @@ const ActivationKeysList = () => {
const [createActivationKey, { isLoading: isLoadingActivationKey }] =
useCreateActivationKeysMutation();
const recentActivationKey = window.localStorage.getItem(
'imageBuilder.recentActivationKey'
);
useEffect(() => {
if (isProd()) {
dispatch(changeServerUrl('subscription.rhsm.redhat.com'));
@ -175,13 +141,10 @@ const ActivationKeysList = () => {
selection: string
) => {
setIsOpen(false);
window.localStorage.setItem('imageBuilder.recentActivationKey', selection);
dispatch(changeActivationKey(selection));
};
const handleClear = () => {
dispatch(changeActivationKey(undefined));
};
const handleToggle = () => {
if (!isOpen) {
refetch();
@ -193,11 +156,16 @@ const ActivationKeysList = () => {
try {
await createActivationKey({
body: {
name: 'activation-key-default',
name: defaultActivationKeyName,
serviceLevel: 'Self-Support',
},
});
window.localStorage.setItem(
'imageBuilder.recentActivationKey',
defaultActivationKeyName
);
refetch();
dispatch(changeActivationKey(defaultActivationKeyName));
} catch (error) {
dispatch(
addNotification({
@ -209,17 +177,34 @@ const ActivationKeysList = () => {
}
};
const isActivationKeysEmpty =
isSuccessActivationKeys &&
!isLoadingActivationKey &&
activationKeys.body?.length === 0;
if (isActivationKeysEmpty) {
handleCreateActivationKey();
}
useEffect(() => {
if (!activationKey && isSuccessActivationKeys) {
if (
recentActivationKey &&
activationKeys?.body?.find((key) => key.name === recentActivationKey)
) {
dispatch(changeActivationKey(recentActivationKey));
} else if (
activationKeys &&
activationKeys.body &&
activationKeys.body.length > 0
) {
dispatch(changeActivationKey(activationKeys?.body[0].name));
}
}
}, [isSuccessActivationKeys]);
const setSelectOptions = () => {
const selectOptions = [];
if (isActivationKeysEmpty) {
selectOptions.push(
<EmptyActivationsKeyState
handleActivationKeyFn={handleCreateActivationKey}
isLoading={isLoadingActivationKey}
key={'Empty'}
/>
);
}
if (isSuccessActivationKeys) {
activationKeys.body?.map((key, index) =>
selectOptions.push(<SelectOption key={index} value={key.name} />)
@ -240,13 +225,9 @@ const ActivationKeysList = () => {
return selectOptions;
};
const isActivationKeysEmpty =
isSuccessActivationKeys && activationKeys.body?.length === 0;
return (
<>
<FormGroup
isRequired={true}
label={
<>
Activation key to use for this image <PopoverActivation />
@ -259,20 +240,19 @@ const ActivationKeysList = () => {
variant={SelectVariant.typeahead}
onToggle={handleToggle}
onSelect={setActivationKey}
onClear={handleClear}
selections={activationKey}
isOpen={isOpen}
placeholderText="Select activation key"
typeAheadAriaLabel="Select activation key"
isDisabled={!isSuccessActivationKeys}
isDisabled={
!isSuccessActivationKeys || registrationType === 'register-later'
}
>
{setSelectOptions()}
</Select>
<TextContent>
<Text>
By default, an activation key is generated and preset for you.
Admins can create and manage keys by visiting the{' '}
<ManageKeysButton />
Create and manage activation keys in the <ManageKeysButton />
</Text>
</TextContent>
</FormGroup>

View file

@ -1,41 +0,0 @@
import React from 'react';
import {
Button,
Text,
TextContent,
TextVariants,
} from '@patternfly/react-core';
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
import { RHC_URL } from '../../../../constants';
const RegisterLaterInformation = () => {
return (
<TextContent>
<Text component={TextVariants.h3}>Register later</Text>
<Text>
On initial boot, systems will need to be registered manually before
having access to updates or Red Hat services. Registering and connecting
your systems during the image creation is recommended.
</Text>
<Text>
If you prefer to register later, review the instructions for manual
registration with remote host configuration.
</Text>
<Button
component="a"
target="_blank"
variant="link"
icon={<ExternalLinkAltIcon />}
iconPosition="right"
isInline
href={RHC_URL}
>
Registering with remote host configuration
</Button>
</TextContent>
);
};
export default RegisterLaterInformation;

View file

@ -5,62 +5,18 @@ import {
Checkbox,
FormGroup,
Popover,
Radio,
Text,
TextContent,
} from '@patternfly/react-core';
import { HelpIcon, ExternalLinkAltIcon } from '@patternfly/react-icons';
import { ExternalLinkAltIcon, HelpIcon } from '@patternfly/react-icons';
import {
INSIGHTS_URL,
RHC_URL,
SUBSCRIPTION_MANAGEMENT_URL,
} from '../../../../constants';
import { INSIGHTS_URL, RHC_URL } from '../../../../constants';
import { useAppDispatch, useAppSelector } from '../../../../store/hooks';
import {
changeActivationKey,
changeRegistrationType,
selectRegistrationType,
} from '../../../../store/wizardSlice';
const RHSMPopover = () => {
return (
<Popover
headerContent="About Red Hat Subscription Management"
position="right"
minWidth="30rem"
bodyContent={
<TextContent>
<Text>
Registered systems are entitled to support services, errata,
patches, and upgrades.
</Text>
<Button
component="a"
target="_blank"
variant="link"
icon={<ExternalLinkAltIcon />}
iconPosition="right"
isInline
href={SUBSCRIPTION_MANAGEMENT_URL}
>
Learn more about Red Hat Subscription Management
</Button>
</TextContent>
}
>
<Button
variant="plain"
className="pf-u-pl-sm pf-u-pt-0 pf-u-pb-0"
aria-label="About remote host configuration (rhc)"
isInline
>
<HelpIcon />
</Button>
</Popover>
);
};
const InsightsPopover = () => {
return (
<Popover
@ -142,47 +98,44 @@ const RhcPopover = () => {
const Registration = () => {
const dispatch = useAppDispatch();
const registrationType = useAppSelector(selectRegistrationType);
const [showOptions, setShowOptions] = useState(
registrationType === 'register-now-insights' ||
registrationType === 'register-now'
registrationType === 'register-later'
);
return (
<FormGroup label="Registration method">
<Radio
autoFocus
label={
(!showOptions &&
'Automatically register and enable advanced capabilities') || (
<>
Monitor & manage subscriptions and access to Red Hat content
<RHSMPopover />
</>
)
<Checkbox
label="Automatically register and enable advanced capabilities"
data-testid="automatically-register-checkbox"
isChecked={
registrationType === 'register-now' ||
registrationType === 'register-now-insights' ||
registrationType === 'register-now-rhc'
}
data-testid="registration-radio-now"
name="register-system"
id="register-system-now"
isChecked={registrationType.startsWith('register-now')}
onChange={() => {
dispatch(changeRegistrationType('register-now-rhc'));
onChange={(_event, checked) => {
if (checked) {
dispatch(changeRegistrationType('register-now'));
} else {
dispatch(changeRegistrationType('register-later'));
setShowOptions(false);
}
}}
id="register-system-now"
name="register-system-now"
autoFocus
description={
!showOptions && (
<Button
component="a"
data-testid="registration-additional-options"
variant="link"
isDisabled={!registrationType.startsWith('register-now')}
isInline
onClick={() => setShowOptions(!showOptions)}
>
Show additional connection options
</Button>
)
<Button
component="a"
data-testid="registration-additional-options"
variant="link"
isDisabled={!registrationType.startsWith('register-now')}
isInline
onClick={() => setShowOptions(!showOptions)}
>
{`${!showOptions ? 'Show' : 'Hide'} additional connection options`}
</Button>
}
body={
showOptions && (
@ -234,19 +187,6 @@ const Registration = () => {
)
}
/>
<Radio
name="register-system"
className="pf-u-mt-md"
data-testid="registration-radio-later"
id="register-system-later"
label="Register later"
isChecked={registrationType === 'register-later'}
onChange={() => {
setShowOptions(false);
dispatch(changeRegistrationType('register-later'));
dispatch(changeActivationKey(undefined));
}}
/>
</FormGroup>
);
};

View file

@ -4,7 +4,6 @@ import { Text, Form, Title, FormGroup } from '@patternfly/react-core';
import ActivationKeyInformation from './ActivationKeyInformation';
import ActivationKeysList from './ActivationKeysList';
import RegisterLaterInformation from './RegisterLaterInformation';
import Registration from './Registration';
import { useAppSelector } from '../../../../store/hooks';
@ -14,34 +13,27 @@ import {
} from '../../../../store/wizardSlice';
const RegistrationStep = () => {
const registrationType = useAppSelector(selectRegistrationType);
const activationKey = useAppSelector(selectActivationKey);
const registrationType = useAppSelector(selectRegistrationType);
return (
<Form>
<Title headingLevel="h1" size="xl">
Register systems using this image
</Title>
<Text>
To enhance security and track your spending, you can register your
systems automatically with Red Hat now, or manually during the initial
boot later.
You can either automatically register your systems with Red Hat to
enhance security and track your spending or choose to register your
system during initial boot.
</Text>
<Registration />
{registrationType !== 'register-later' ? (
<>
<ActivationKeysList />
{activationKey && (
<FormGroup
isRequired={true}
label={'Selected activation key'}
data-testid="selected-activation-key"
>
<ActivationKeyInformation />
</FormGroup>
)}
</>
) : (
<RegisterLaterInformation />
<ActivationKeysList />
{activationKey && registrationType !== 'register-later' && (
<FormGroup
label={'Selected activation key'}
data-testid="selected-activation-key"
>
<ActivationKeyInformation />
</FormGroup>
)}
</Form>
);