V2Wizard: Add components to the Registration step

This migrates all the needed components from Javascript and DDF to Typescript and RTK.

New slices were added for registration type and activation key.

Disabling the "Next" button should work correctly now.
This commit is contained in:
regexowl 2024-01-12 09:17:24 +01:00 committed by Klara Simickova
parent 9ce438295d
commit 360984df7c
8 changed files with 513 additions and 304 deletions

View file

@ -10,6 +10,7 @@ import {
import { useNavigate } from 'react-router-dom';
import ImageOutputStep from './steps/ImageOutput';
import RegistrationStep from './steps/Registration';
import Aws from './steps/TargetEnvironment/Aws';
import Gcp from './steps/TargetEnvironment/Gcp';
import { isAwsAccountIdValid, isGcpEmailValid } from './validators';
@ -18,12 +19,14 @@ import { useAppDispatch, useAppSelector } from '../../store/hooks';
import './CreateImageWizard.scss';
import {
initializeWizard,
selectActivationKey,
selectAwsAccountId,
selectAwsShareMethod,
selectAwsSource,
selectGcpEmail,
selectGcpShareMethod,
selectImageTypes,
selectRegistrationType,
} from '../../store/wizardSlice';
import { resolveRelPath } from '../../Utilities/path';
import { ImageBuilderHeader } from '../sharedComponents/ImageBuilderHeader';
@ -77,6 +80,11 @@ const CreateImageWizard = () => {
const gcpShareMethod = useAppSelector((state) => selectGcpShareMethod(state));
const gcpEmail = useAppSelector((state) => selectGcpEmail(state));
const registrationType = useAppSelector((state) =>
selectRegistrationType(state)
);
const activationKey = useAppSelector((state) => selectActivationKey(state));
return (
<>
<ImageBuilderHeader />
@ -138,6 +146,19 @@ const CreateImageWizard = () => {
</WizardStep>,
]}
/>
<WizardStep
name="Register"
id="step-register"
footer={
<CustomWizardFooter
disableNext={
registrationType !== 'register-later' && !activationKey
}
/>
}
>
<RegistrationStep />
</WizardStep>
<WizardStep
name="Review"
id="step-review"

View file

@ -1,9 +1,8 @@
import React, { useContext } from 'react';
import React from 'react';
import { useFormApi } from '@data-driven-forms/react-form-renderer';
import WizardContext from '@data-driven-forms/react-form-renderer/wizard-context';
import {
Alert,
FormGroup,
Spinner,
Text,
TextContent,
@ -17,13 +16,12 @@ import { Button, Popover } from '@patternfly/react-core';
import { HelpIcon } from '@patternfly/react-icons';
import { Table, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table';
import { useShowActivationKeyQuery } from '../../../store/rhsmApi';
import { useAppSelector } from '../../../../store/hooks';
import { useShowActivationKeyQuery } from '../../../../store/rhsmApi';
import { selectActivationKey } from '../../../../store/wizardSlice';
const ActivationKeyInformation = (): JSX.Element => {
const { getState } = useFormApi();
const { currentStep } = useContext(WizardContext);
const activationKey = getState()?.values?.['subscription-activation-key'];
const activationKey = useAppSelector((state) => selectActivationKey(state));
const {
data: activationKeyInfo,
@ -31,7 +29,7 @@ const ActivationKeyInformation = (): JSX.Element => {
isSuccess: isSuccessActivationKeyInfo,
isError: isErrorActivationKeyInfo,
} = useShowActivationKeyQuery(
{ name: activationKey },
{ name: activationKey! },
{
skip: !activationKey,
}
@ -41,105 +39,113 @@ const ActivationKeyInformation = (): JSX.Element => {
<>
{isFetchingActivationKeyInfo && <Spinner size="lg" />}
{isSuccessActivationKeyInfo && (
<TextContent>
<TextList component={TextListVariants.dl}>
<TextListItem component={TextListItemVariants.dt}>
Name:
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{activationKey}
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
Role:
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{activationKeyInfo.body?.role || 'Not defined'}
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
SLA:
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{activationKeyInfo.body?.serviceLevel || 'Not defined'}
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
Usage:
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{activationKeyInfo.body?.usage || 'Not defined'}
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
Additional repositories:
<Popover
bodyContent={
<TextContent>
<Text>
The core repositories for your operating system version
are always enabled and do not need to be explicitly added
to the activation key.
</Text>
</TextContent>
}
>
<Button
variant="plain"
aria-label="About additional repositories"
className="pf-u-pl-sm pf-u-pt-0 pf-u-pb-0"
size="sm"
>
<HelpIcon />
</Button>
</Popover>
</TextListItem>
<TextListItem
component={TextListItemVariants.dd}
className="pf-u-display-flex pf-u-align-items-flex-end"
>
{activationKeyInfo.body?.additionalRepositories &&
activationKeyInfo.body?.additionalRepositories?.length > 0 ? (
<FormGroup
isRequired={true}
label={'Selected activation key'}
data-testid="selected-activation-key"
>
<TextContent>
<TextList component={TextListVariants.dl}>
<TextListItem component={TextListItemVariants.dt}>
Name:
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{activationKey}
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
Role:
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{activationKeyInfo.body?.role || 'Not defined'}
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
SLA:
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{activationKeyInfo.body?.serviceLevel || 'Not defined'}
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
Usage:
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{activationKeyInfo.body?.usage || 'Not defined'}
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
Additional repositories:
<Popover
bodyContent={
<TextContent>
<Text component={TextVariants.h3}>
Additional repositories
<Text>
The core repositories for your operating system version
are always enabled and do not need to be explicitly
added to the activation key.
</Text>
<Table
aria-label="Additional repositories table"
variant="compact"
>
<Thead>
<Tr>
<Th>Name</Th>
</Tr>
</Thead>
<Tbody data-testid="additional-repositories-table">
{activationKeyInfo.body?.additionalRepositories?.map(
(repo, index) => (
<Tr key={index}>
<Td>{repo.repositoryLabel}</Td>
</Tr>
)
)}
</Tbody>
</Table>
</TextContent>
}
>
<Button
data-testid="repositories-popover-button"
variant="link"
aria-label="Show additional repositories"
className="pf-u-pl-0 pf-u-pt-0 pf-u-pb-0"
variant="plain"
aria-label="About additional repositories"
className="pf-u-pl-sm pf-u-pt-0 pf-u-pb-0"
size="sm"
>
{activationKeyInfo.body?.additionalRepositories?.length}{' '}
repositories
<HelpIcon />
</Button>
</Popover>
) : (
'None'
)}
</TextListItem>
</TextList>
</TextContent>
</TextListItem>
<TextListItem
component={TextListItemVariants.dd}
className="pf-u-display-flex pf-u-align-items-flex-end"
>
{activationKeyInfo.body?.additionalRepositories &&
activationKeyInfo.body?.additionalRepositories?.length > 0 ? (
<Popover
position="right"
minWidth="30rem"
bodyContent={
<TextContent>
<Text component={TextVariants.h3}>
Additional repositories
</Text>
<Table
aria-label="Additional repositories table"
variant="compact"
>
<Thead>
<Tr>
<Th>Name</Th>
</Tr>
</Thead>
<Tbody data-testid="additional-repositories-table">
{activationKeyInfo.body?.additionalRepositories?.map(
(repo, index) => (
<Tr key={index}>
<Td>{repo.repositoryLabel}</Td>
</Tr>
)
)}
</Tbody>
</Table>
</TextContent>
}
>
<Button
data-testid="repositories-popover-button"
variant="link"
aria-label="Show additional repositories"
className="pf-u-pl-0 pf-u-pt-0 pf-u-pb-0"
>
{activationKeyInfo.body?.additionalRepositories?.length}{' '}
repositories
</Button>
</Popover>
) : (
'None'
)}
</TextListItem>
</TextList>
</TextContent>
</FormGroup>
)}
{isErrorActivationKeyInfo && (
<TextContent>
@ -151,10 +157,6 @@ const ActivationKeyInformation = (): JSX.Element => {
{activationKey}
</TextListItem>
</TextList>
</TextContent>
)}
{isErrorActivationKeyInfo && currentStep.name === 'registration' && (
<>
<br />
<Alert
title="Information about the activation key unavailable"
@ -165,7 +167,7 @@ const ActivationKeyInformation = (): JSX.Element => {
Information about the activation key cannot be loaded. Please check
the key was not removed and try again later.
</Alert>
</>
</TextContent>
)}
</>
);

View file

@ -1,7 +1,5 @@
import React, { useEffect, useState } from 'react';
import useFieldApi from '@data-driven-forms/react-form-renderer/use-field-api';
import useFormApi from '@data-driven-forms/react-form-renderer/use-form-api';
import {
Alert,
FormGroup,
@ -13,6 +11,9 @@ import {
EmptyStateHeader,
EmptyStateFooter,
EmptyStateActions,
Text,
TextContent,
Popover,
} from '@patternfly/react-core';
import {
Select,
@ -20,17 +21,74 @@ import {
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';
import PropTypes from 'prop-types';
import { useDispatch } from 'react-redux';
import { useAppDispatch, useAppSelector } from '../../../../store/hooks';
import {
useListActivationKeysQuery,
useCreateActivationKeysMutation,
} from '../../../store/rhsmApi';
import { useGetEnvironment } from '../../../Utilities/useGetEnvironment';
} from '../../../../store/rhsmApi';
import {
changeActivationKey,
changeBaseUrl,
changeServerUrl,
selectActivationKey,
} from '../../../../store/wizardSlice';
import { useGetEnvironment } from '../../../../Utilities/useGetEnvironment';
const EmptyActivationsKeyState = ({ handleActivationKeyFn, isLoading }) => (
const PopoverActivation = () => {
const [orgId, setOrgId] = useState<string | undefined>(undefined);
const { auth } = useChrome();
useEffect(() => {
(async () => {
const userData = await auth?.getUser();
const id = userData?.identity?.internal?.org_id;
setOrgId(id);
})();
});
return (
<Popover
hasAutoWidth
maxWidth="35rem"
bodyContent={
<TextContent>
<Text>
Activation keys enable you to register a system with appropriate
subscriptions, system purpose, and repositories attached.
</Text>
<Text>
If using an activation key with command line registration, you must
provide your organization&apos;s ID.
{orgId && <br />}
{orgId && "Your organization's ID is " + orgId}
</Text>
</TextContent>
}
>
<Button
variant="plain"
aria-label="Activation key popover"
aria-describedby="subscription-activation-key"
className="pf-c-form__group-label-help"
>
<HelpIcon />
</Button>
</Popover>
);
};
type EmptyActivationsKeyStateProps = {
handleActivationKeyFn: Function;
isLoading: boolean;
};
const EmptyActivationsKeyState = ({
handleActivationKeyFn,
isLoading,
}: EmptyActivationsKeyStateProps) => (
<EmptyState variant="xs">
<EmptyStateHeader
titleText="No activation keys found"
@ -44,7 +102,7 @@ const EmptyActivationsKeyState = ({ handleActivationKeyFn, isLoading }) => (
<EmptyStateFooter>
<EmptyStateActions>
<Button
onClick={handleActivationKeyFn}
onClick={() => handleActivationKeyFn()}
icon={<AddCircleOIcon />}
isLoading={isLoading}
iconPosition="left"
@ -57,21 +115,34 @@ const EmptyActivationsKeyState = ({ handleActivationKeyFn, isLoading }) => (
</EmptyState>
);
EmptyActivationsKeyState.propTypes = {
handleActivationKeyFn: PropTypes.func.isRequired,
isLoading: PropTypes.bool,
const ManageKeysButton = () => {
const { isProd } = useGetEnvironment();
return (
<Button
component="a"
target="_blank"
variant="link"
icon={<ExternalLinkAltIcon />}
iconPosition="right"
isInline
href={
isProd()
? 'https://console.redhat.com/insights/connector/activation-keys'
: 'https://console.stage.redhat.com/insights/connector/activation-keys'
}
>
Activation keys page
</Button>
);
};
const ActivationKeys = ({ label, isRequired, ...props }) => {
const { isProd } = useGetEnvironment();
const { change, getState } = useFormApi();
const { input } = useFieldApi(props);
const [isOpen, setIsOpen] = useState(false);
const [activationKeySelected, selectActivationKey] = useState(
getState()?.values?.['subscription-activation-key']
);
const ActivationKeysList = () => {
const dispatch = useAppDispatch();
const dispatch = useDispatch();
const activationKey = useAppSelector((state) => selectActivationKey(state));
const { isProd } = useGetEnvironment();
const [isOpen, setIsOpen] = useState(false);
const {
data: activationKeys,
@ -83,25 +154,27 @@ const ActivationKeys = ({ label, isRequired, ...props }) => {
const [createActivationKey, { isLoading: isLoadingActivationKey }] =
useCreateActivationKeysMutation();
useEffect(() => {
if (isProd()) {
change('subscription-server-url', 'subscription.rhsm.redhat.com');
change('subscription-base-url', 'https://cdn.redhat.com/');
dispatch(changeServerUrl('subscription.rhsm.redhat.com'));
dispatch(changeBaseUrl('https://cdn.redhat.com/'));
} else {
change('subscription-server-url', 'subscription.rhsm.stage.redhat.com');
change('subscription-base-url', 'https://cdn.stage.redhat.com/');
dispatch(changeServerUrl('subscription.rhsm.stage.redhat.com'));
dispatch(changeBaseUrl('https://cdn.stage.redhat.com/'));
}
}, [isProd, change]);
}, [dispatch, isProd]);
const setActivationKey = (_, selection) => {
selectActivationKey(selection);
const setActivationKey = (
_event: React.MouseEvent<Element, MouseEvent>,
selection: string
) => {
setIsOpen(false);
change(input.name, selection);
dispatch(changeActivationKey(selection));
};
const handleClear = () => {
selectActivationKey();
change(input.name, undefined);
dispatch(changeActivationKey(undefined));
};
const handleToggle = () => {
@ -112,32 +185,68 @@ const ActivationKeys = ({ label, isRequired, ...props }) => {
};
const handleCreateActivationKey = async () => {
const res = await createActivationKey({
body: {
name: 'activation-key-default',
serviceLevel: 'Self-Support',
},
});
refetch();
if (res.error) {
try {
await createActivationKey({
body: {
name: 'activation-key-default',
serviceLevel: 'Self-Support',
},
});
refetch();
} catch (error) {
dispatch(
addNotification({
variant: 'danger',
title: 'Error creating activation key',
description: res.error?.data?.error?.message,
description: error?.data?.error?.message,
})
);
}
};
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} />)
);
}
if (!isSuccessActivationKeys && isFetchingActivationKeys) {
selectOptions.push(
<SelectOption
key={'Fetching'}
isNoResultsOption={true}
data-testid="activation-keys-loading"
>
<Spinner size="md" />
</SelectOption>
);
}
return selectOptions;
};
const isActivationKeysEmpty =
isSuccessActivationKeys && activationKeys.body.length === 0;
isSuccessActivationKeys && activationKeys.body?.length === 0;
return (
<>
<FormGroup
isRequired={isRequired}
label={label}
isRequired={true}
label={
<>
Activation key to use for this image <PopoverActivation />
</>
}
data-testid="subscription-activation-key"
>
<Select
@ -146,31 +255,20 @@ const ActivationKeys = ({ label, isRequired, ...props }) => {
onToggle={handleToggle}
onSelect={setActivationKey}
onClear={handleClear}
selections={activationKeySelected}
selections={activationKey}
isOpen={isOpen}
placeholderText="Select activation key"
typeAheadAriaLabel="Select activation key"
isDisabled={!isSuccessActivationKeys}
>
{isActivationKeysEmpty && (
<EmptyActivationsKeyState
handleActivationKeyFn={handleCreateActivationKey}
isLoading={isLoadingActivationKey}
/>
)}
{isSuccessActivationKeys &&
activationKeys.body.map((key, index) => (
<SelectOption key={index} value={key.name} />
))}
{!isSuccessActivationKeys && isFetchingActivationKeys && (
<SelectOption
isNoResultsOption={true}
data-testid="activation-keys-loading"
>
<Spinner size="md" />
</SelectOption>
)}
{setSelectOptions()}
</Select>
<TextContent>
<Text>
By default, activation key is generated and preset for you. Admins
can create and manage keys by visiting the <ManageKeysButton />
</Text>
</TextContent>
</FormGroup>
{isErrorActivationKeys && (
<Alert
@ -186,14 +284,4 @@ const ActivationKeys = ({ label, isRequired, ...props }) => {
);
};
ActivationKeys.propTypes = {
label: PropTypes.node,
isRequired: PropTypes.bool,
};
ActivationKeys.defaultProps = {
label: '',
isRequired: false,
};
export default ActivationKeys;
export default ActivationKeysList;

View file

@ -0,0 +1,39 @@
import React from 'react';
import {
Button,
Text,
TextContent,
TextVariants,
} from '@patternfly/react-core';
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
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="https://access.redhat.com/articles/rhc"
>
Registering with remote host configuration
</Button>
</TextContent>
);
};
export default RegisterLaterInformation;

View file

@ -1,8 +1,5 @@
import React, { useState } from 'react';
import { FormSpy } from '@data-driven-forms/react-form-renderer';
import useFieldApi from '@data-driven-forms/react-form-renderer/use-field-api';
import useFormApi from '@data-driven-forms/react-form-renderer/use-form-api';
import {
Button,
Checkbox,
@ -13,7 +10,13 @@ import {
TextContent,
} from '@patternfly/react-core';
import { HelpIcon, ExternalLinkAltIcon } from '@patternfly/react-icons';
import PropTypes from 'prop-types';
import { useAppDispatch, useAppSelector } from '../../../../store/hooks';
import {
changeActivationKey,
changeRegistrationType,
selectRegistrationType,
} from '../../../../store/wizardSlice';
const RHSMPopover = () => {
return (
@ -132,121 +135,117 @@ const RhcPopover = () => {
);
};
const Registration = ({ label, ...props }) => {
const { change, getState } = useFormApi();
const { input } = useFieldApi(props);
const registerSystem = getState()?.values?.['register-system'];
const Registration = () => {
const dispatch = useAppDispatch();
const registrationType = useAppSelector((state) =>
selectRegistrationType(state)
);
const [showOptions, setShowOptions] = useState(
registerSystem === 'register-now-insights' ||
registerSystem === 'register-now'
registrationType === 'register-now-insights' ||
registrationType === 'register-now'
);
return (
<FormSpy>
{() => (
<FormGroup label={label}>
<Radio
autoFocus
label={
(!showOptions &&
'Automatically register and enable advanced capabilities') || (
<FormGroup label="Registration method">
<Radio
autoFocus
label={
(!showOptions &&
'Automatically register and enable advanced capabilities') || (
<>
Monitor & manage subscriptions and access to Red Hat content
<RHSMPopover />
</>
)
}
data-testid="registration-radio-now"
name="register-system"
id="register-system-now"
isChecked={registrationType.startsWith('register-now')}
onChange={() => {
dispatch(changeRegistrationType('register-now-rhc'));
}}
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>
)
}
body={
showOptions && (
<Checkbox
className="pf-u-ml-lg"
label={
<>
Monitor & manage subscriptions and access to Red Hat content
<RHSMPopover />
Enable predictive analytics and management capabilities
<InsightsPopover />
</>
)
}
data-testid="registration-radio-now"
name="register-system"
id="register-system-now"
isChecked={registerSystem.startsWith('register-now')}
onChange={() => {
change(input.name, 'register-now-rhc');
}}
description={
!showOptions && (
<Button
component="a"
data-testid="registration-additional-options"
variant="link"
isDisabled={!registerSystem.startsWith('register-now')}
isInline
onClick={() => setShowOptions(!showOptions)}
>
Show additional connection options
</Button>
)
}
body={
showOptions && (
}
data-testid="registration-checkbox-insights"
isChecked={
registrationType === 'register-now-insights' ||
registrationType === 'register-now-rhc'
}
onChange={(_event, checked) => {
if (checked) {
dispatch(changeRegistrationType('register-now-insights'));
} else {
dispatch(changeRegistrationType('register-now'));
}
}}
id="register-system-now-insights"
name="register-system-insights"
body={
<Checkbox
className="pf-u-ml-lg"
label={
<>
Enable predictive analytics and management capabilities
<InsightsPopover />
Enable remote remediations and system management with
automation
<RhcPopover />
</>
}
data-testid="registration-checkbox-insights"
isChecked={
registerSystem === 'register-now-insights' ||
registerSystem === 'register-now-rhc'
}
data-testid="registration-checkbox-rhc"
isChecked={registrationType === 'register-now-rhc'}
onChange={(_event, checked) => {
if (checked) {
change(input.name, 'register-now-insights');
dispatch(changeRegistrationType('register-now-rhc'));
} else {
change(input.name, 'register-now');
dispatch(changeRegistrationType('register-now-insights'));
}
}}
id="register-system-now-insights"
name="register-system-insights"
body={
<Checkbox
label={
<>
Enable remote remediations and system management with
automation
<RhcPopover />
</>
}
data-testid="registration-checkbox-rhc"
isChecked={registerSystem === 'register-now-rhc'}
onChange={(_event, checked) => {
if (checked) {
change(input.name, 'register-now-rhc');
} else {
change(input.name, 'register-now-insights');
}
}}
id="register-system-now-rhc"
name="register-system-rhc"
/>
}
id="register-system-now-rhc"
name="register-system-rhc"
/>
)
}
/>
<Radio
name="register-system"
className="pf-u-mt-md"
data-testid="registration-radio-later"
id="register-system-later"
label="Register later"
isChecked={registerSystem === 'register-later'}
onChange={() => {
setShowOptions(false);
change(input.name, 'register-later');
}}
/>
</FormGroup>
)}
</FormSpy>
}
/>
)
}
/>
<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>
);
};
Registration.propTypes = {
label: PropTypes.node,
};
export default Registration;

View file

@ -1,29 +0,0 @@
import React from 'react';
import { FormSpy } from '@data-driven-forms/react-form-renderer';
import { FormGroup } from '@patternfly/react-core';
import { isEmpty } from 'lodash';
import PropTypes from 'prop-types';
import ActivationKeyInformation from './ActivationKeyInformation';
const RegistrationKeyInformation = ({ label, valueReference }) => {
return (
<FormSpy>
{({ values }) =>
isEmpty(values[valueReference]) ? null : (
<FormGroup label={label}>
<ActivationKeyInformation />
</FormGroup>
)
}
</FormSpy>
);
};
RegistrationKeyInformation.propTypes = {
label: PropTypes.node,
valueReference: PropTypes.node,
};
export default RegistrationKeyInformation;

View file

@ -0,0 +1,37 @@
import React from 'react';
import { Text, Form, Title } 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';
import { selectRegistrationType } from '../../../../store/wizardSlice';
const RegistrationStep = () => {
const registrationType = useAppSelector((state) =>
selectRegistrationType(state)
);
return (
<Form>
<Title headingLevel="h2">Register systems using this image</Title>
<Text>
Automatically register your systems with Red Hat to enhance security and
track your spending.
</Text>
<Registration />
{registrationType !== 'register-later' ? (
<>
<ActivationKeysList />
<ActivationKeyInformation />
</>
) : (
<RegisterLaterInformation />
)}
</Form>
);
};
export default RegistrationStep;

View file

@ -1,6 +1,7 @@
import { PayloadAction, createSlice } from '@reduxjs/toolkit';
import { Distributions, ImageRequest, ImageTypes } from './imageBuilderApi';
import { ActivationKeys } from './rhsmApi';
import {
AwsShareMethod,
@ -15,6 +16,10 @@ import { RHEL_9, X86_64 } from '../constants';
import { RootState } from '.';
type wizardState = {
env: {
serverUrl: string | undefined;
baseUrl: string | undefined;
};
architecture: ImageRequest['architecture'];
distribution: Distributions;
imageTypes: ImageTypes[];
@ -28,9 +33,17 @@ type wizardState = {
accountType: GcpAccountType;
email: string | undefined;
};
registration: {
registrationType: string;
activationKey: ActivationKeys['name'];
};
};
const initialState: wizardState = {
env: {
serverUrl: undefined,
baseUrl: undefined,
},
architecture: X86_64,
distribution: RHEL_9,
imageTypes: [],
@ -44,6 +57,18 @@ const initialState: wizardState = {
accountType: 'google',
email: undefined,
},
registration: {
registrationType: 'register-now-rhc',
activationKey: '',
},
};
export const selectServerUrl = (state: RootState) => {
return state.wizard.env.serverUrl;
};
export const selectBaseUrl = (state: RootState) => {
return state.wizard.env.baseUrl;
};
export const selectArchitecture = (state: RootState) => {
@ -84,11 +109,25 @@ export const selectGcpEmail = (state: RootState) => {
return state.wizard.gcp.email;
};
export const selectRegistrationType = (state: RootState) => {
return state.wizard.registration.registrationType;
};
export const selectActivationKey = (state: RootState) => {
return state.wizard.registration.activationKey;
};
export const wizardSlice = createSlice({
name: 'wizard',
initialState,
reducers: {
initializeWizard: () => initialState,
changeServerUrl: (state, action: PayloadAction<string | undefined>) => {
state.env.serverUrl = action.payload;
},
changeBaseUrl: (state, action: PayloadAction<string | undefined>) => {
state.env.baseUrl = action.payload;
},
changeArchitecture: (
state,
action: PayloadAction<ImageRequest['architecture']>
@ -142,11 +181,22 @@ export const wizardSlice = createSlice({
changeGcpEmail: (state, action: PayloadAction<string | undefined>) => {
state.gcp.email = action.payload;
},
changeRegistrationType: (state, action: PayloadAction<string>) => {
state.registration.registrationType = action.payload;
},
changeActivationKey: (
state,
action: PayloadAction<ActivationKeys['name']>
) => {
state.registration.activationKey = action.payload;
},
},
});
export const {
initializeWizard,
changeServerUrl,
changeBaseUrl,
changeArchitecture,
changeDistribution,
addImageType,
@ -158,5 +208,7 @@ export const {
changeGcpShareMethod,
changeGcpAccountType,
changeGcpEmail,
changeRegistrationType,
changeActivationKey,
} = wizardSlice.actions;
export default wizardSlice.reducer;