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:
parent
c4bbd413b3
commit
2caddb66c7
5 changed files with 96 additions and 223 deletions
|
|
@ -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
|
||||
}
|
||||
/>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue