diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/OcmOpenShiftVersion.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/OcmOpenShiftVersion.tsx index 3e0036d7fc..c2cf548a58 100644 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/OcmOpenShiftVersion.tsx +++ b/libs/ui-lib/lib/ocm/components/clusterConfiguration/OcmOpenShiftVersion.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { Stack, StackItem } from '@patternfly/react-core'; import { ClusterCpuArchitecture, getOpenshiftVersionText, @@ -7,9 +8,9 @@ import { } from '../../../common'; type OcmOpenShiftVersionProps = { - versions: OpenshiftVersionOptionType[]; + versions?: OpenshiftVersionOptionType[]; openshiftVersion: string; - clusterCpuArchitecture: string | undefined; + clusterCpuArchitecture?: string; withPreviewText?: boolean; withMultiText?: boolean; }; @@ -20,17 +21,23 @@ const OcmOpenShiftVersion = ({ versions, withPreviewText, withMultiText, -}: OcmOpenShiftVersionProps) => { + children, +}: React.PropsWithChildren) => { return ( - OpenShift{' '} - {getOpenshiftVersionText({ - openshiftVersion, - cpuArchitecture: clusterCpuArchitecture as ClusterCpuArchitecture, - versions, - withPreviewText, - withMultiText, - })} + + + OpenShift{' '} + {getOpenshiftVersionText({ + openshiftVersion, + cpuArchitecture: clusterCpuArchitecture as ClusterCpuArchitecture, + versions, + withPreviewText, + withMultiText, + })} + + {children && {children}} + ); }; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/staticIp/components/FormViewNetworkWide/formViewNetworkWideValidationSchema.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/staticIp/components/FormViewNetworkWide/formViewNetworkWideValidationSchema.tsx index fc37a77c76..07d5fe3da7 100644 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/staticIp/components/FormViewNetworkWide/formViewNetworkWideValidationSchema.tsx +++ b/libs/ui-lib/lib/ocm/components/clusterConfiguration/staticIp/components/FormViewNetworkWide/formViewNetworkWideValidationSchema.tsx @@ -66,7 +66,6 @@ const getMachineNetworkValidationSchema = (protocolVersion: ProtocolVersion) => MAX_PREFIX_LENGTH[protocolVersion], `Prefix length must be less than or equal to ${MAX_PREFIX_LENGTH[protocolVersion]}`, ) - .nullable() .transform(transformNumber) as Yup.NumberSchema, //add casting to not get typescript error caused by nullable }); diff --git a/libs/ui-lib/lib/ocm/components/clusterWizard/ClusterDetails.tsx b/libs/ui-lib/lib/ocm/components/clusterWizard/ClusterDetails.tsx index 2d445a4d46..0a734f5d6c 100644 --- a/libs/ui-lib/lib/ocm/components/clusterWizard/ClusterDetails.tsx +++ b/libs/ui-lib/lib/ocm/components/clusterWizard/ClusterDetails.tsx @@ -132,7 +132,6 @@ const ClusterDetails = ({ cluster, infraEnv }: ClusterDetailsProps) => { managedDomains={managedDomains} ocpVersions={versions} usedClusterNames={usedClusterNames} - moveNext={() => clusterWizardContext.moveNext()} handleClusterUpdate={handleClusterUpdate} handleClusterCreate={handleClusterCreate} navigation={navigation} diff --git a/libs/ui-lib/lib/ocm/components/clusterWizard/ClusterDetailsForm.tsx b/libs/ui-lib/lib/ocm/components/clusterWizard/ClusterDetailsForm.tsx index 9b9f433e3e..dda0c20307 100644 --- a/libs/ui-lib/lib/ocm/components/clusterWizard/ClusterDetailsForm.tsx +++ b/libs/ui-lib/lib/ocm/components/clusterWizard/ClusterDetailsForm.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { useSelector } from 'react-redux'; import { useLocation } from 'react-router-dom-v5-compat'; -import { Grid, GridItem } from '@patternfly/react-core'; +import { Grid, GridItem, Switch } from '@patternfly/react-core'; import isUndefined from 'lodash-es/isUndefined.js'; import { Formik, FormikHelpers } from 'formik'; import { @@ -41,7 +41,6 @@ type ClusterDetailsFormProps = { ocpVersions: OpenshiftVersionOptionType[]; usedClusterNames: string[]; navigation: React.ReactNode; - moveNext: () => void; handleClusterCreate: (params: ClusterCreateParams, addCustomManifests: boolean) => Promise; handleClusterUpdate: ( clusterId: Cluster['id'], @@ -58,13 +57,13 @@ const ClusterDetailsForm = (props: ClusterDetailsFormProps) => { managedDomains, ocpVersions, usedClusterNames = [], - moveNext, handleClusterUpdate, handleClusterCreate, navigation, } = props; - const clusterWizardContext = useClusterWizardContext(); + const { installDisconnected, setInstallDisconnected, customManifestsStep, moveNext } = + useClusterWizardContext(); const { search } = useLocation(); const { isViewerMode } = useSelector(selectCurrentClusterPermissionsState); const { clearAlerts } = useAlerts(); @@ -119,17 +118,9 @@ const ClusterDetailsForm = (props: ClusterDetailsFormProps) => { managedDomains, ocpVersions, urlSearchParams: search, - addCustomManifests: clusterWizardContext.customManifestsStep, + addCustomManifests: customManifestsStep, }), - [ - infraEnv, - cluster, - pullSecret, - managedDomains, - ocpVersions, - search, - clusterWizardContext.customManifestsStep, - ], + [infraEnv, cluster, pullSecret, managedDomains, ocpVersions, search, customManifestsStep], ); const { t } = useTranslation(); @@ -173,6 +164,17 @@ const ClusterDetailsForm = (props: ClusterDetailsFormProps) => { Cluster details + {!isSingleClusterFeatureEnabled && ( + + setInstallDisconnected(checked)} + ouiaId="DisconnectedInstall" + /> + + )} void; + moveNext: () => void; wizardStepIds: ClusterWizardStepsType[]; onUpdateStaticIpView(view: StaticIpView): void; onUpdateHostNetworkConfigType(type: HostsNetworkConfigurationType): void; @@ -18,6 +18,8 @@ export type ClusterWizardContextType = { setWizardPerPage: (perPage: number) => void; updateUISettings: (data: UISettingsValues) => Promise; uiSettings?: UISettingsValues; + installDisconnected: boolean; + setInstallDisconnected: (enabled: boolean) => void; }; export const ClusterWizardContext = React.createContext(null); diff --git a/libs/ui-lib/lib/ocm/components/clusterWizard/ClusterWizardContextProvider.tsx b/libs/ui-lib/lib/ocm/components/clusterWizard/ClusterWizardContextProvider.tsx index 67ca2c5e0c..216e1860a9 100644 --- a/libs/ui-lib/lib/ocm/components/clusterWizard/ClusterWizardContextProvider.tsx +++ b/libs/ui-lib/lib/ocm/components/clusterWizard/ClusterWizardContextProvider.tsx @@ -4,6 +4,7 @@ import { ClusterWizardContextType, ClusterWizardContext } from './ClusterWizardC import { ClusterWizardFlowStateType, ClusterWizardStepsType, + disconnectedSteps, getClusterWizardFirstStep, isStaticIpStep, isStepAfter, @@ -87,9 +88,10 @@ const ClusterWizardContextProvider = ({ }>) => { const isSingleClusterFeatureEnabled = useFeature('ASSISTED_INSTALLER_SINGLE_CLUSTER_FEATURE'); const [currentStepId, setCurrentStepId] = React.useState(); - const [wizardStepIds, setWizardStepIds] = React.useState(); + const [connectedWizardStepIds, setWizardStepIds] = React.useState(); const [wizardPerPage, setWizardPerPage] = React.useState(10); const [customManifestsStep, setCustomManifestsStep] = React.useState(false); + const [installDisconnected, setInstallDisconnected] = React.useState(false); const location = useLocation(); const locationState = location.state as ClusterWizardFlowStateType | undefined; const { @@ -101,6 +103,8 @@ const ClusterWizardContextProvider = ({ const { clearAlerts, addAlert, alerts } = useAlerts(); const setClusterPermissions = useSetClusterPermissions(); + const wizardStepIds = installDisconnected ? disconnectedSteps : connectedWizardStepIds; + React.useEffect(() => { if (!UISettingsLoading) { const staticIpInfo = infraEnv ? getStaticIpInfo(infraEnv) : undefined; @@ -237,7 +241,7 @@ const ClusterWizardContextProvider = ({ ); } }, - wizardStepIds, + wizardStepIds: wizardStepIds, currentStepId, setCurrentStepId: onSetCurrentStepId, customManifestsStep, @@ -246,6 +250,15 @@ const ClusterWizardContextProvider = ({ setWizardPerPage, uiSettings, updateUISettings, + installDisconnected, + setInstallDisconnected: (enabled: boolean) => { + setInstallDisconnected(enabled); + if (enabled) { + onSetCurrentStepId(disconnectedSteps[0]); + } else { + connectedWizardStepIds?.length && onSetCurrentStepId(connectedWizardStepIds[0]); + } + }, }; }, [ wizardStepIds, @@ -257,6 +270,9 @@ const ClusterWizardContextProvider = ({ clearAlerts, uiSettings, updateUISettings, + installDisconnected, + setInstallDisconnected, + connectedWizardStepIds, ]); if (!contextValue) { diff --git a/libs/ui-lib/lib/ocm/components/clusterWizard/NewClusterWizard.tsx b/libs/ui-lib/lib/ocm/components/clusterWizard/NewClusterWizard.tsx index 827b7f6543..7563648747 100644 --- a/libs/ui-lib/lib/ocm/components/clusterWizard/NewClusterWizard.tsx +++ b/libs/ui-lib/lib/ocm/components/clusterWizard/NewClusterWizard.tsx @@ -1,11 +1,27 @@ import classNames from 'classnames'; import React from 'react'; import ClusterDetails from './ClusterDetails'; +import { useClusterWizardContext } from './ClusterWizardContext'; +import ReviewStep from './disconnected/ReviewStep'; +import BasicStep from './disconnected/BasicStep'; +import { ClusterWizardStepsType } from './wizardTransition'; + +const getCurrentStep = (currentStepId: ClusterWizardStepsType) => { + switch (currentStepId) { + case 'disconnected-review': + return ; + case 'disconnected-basic': + return ; + default: + return ; + } +}; const NewClusterWizard: React.FC = () => { + const { currentStepId } = useClusterWizardContext(); return (
- + {getCurrentStep(currentStepId)}
); }; diff --git a/libs/ui-lib/lib/ocm/components/clusterWizard/constants.ts b/libs/ui-lib/lib/ocm/components/clusterWizard/constants.ts index abe9984c4e..0977dd0e0c 100644 --- a/libs/ui-lib/lib/ocm/components/clusterWizard/constants.ts +++ b/libs/ui-lib/lib/ocm/components/clusterWizard/constants.ts @@ -12,6 +12,8 @@ export const wizardStepNames: { [key in ClusterWizardStepsType]: string } = { 'custom-manifests': 'Custom manifests', review: 'Review and create', 'credentials-download': 'Download credentials', + 'disconnected-review': 'Review and download ISO', + 'disconnected-basic': 'Basic information', }; export const defaultWizardSteps: ClusterWizardStepsType[] = [ diff --git a/libs/ui-lib/lib/ocm/components/clusterWizard/disconnected/BasicStep.tsx b/libs/ui-lib/lib/ocm/components/clusterWizard/disconnected/BasicStep.tsx new file mode 100644 index 0000000000..badf827381 --- /dev/null +++ b/libs/ui-lib/lib/ocm/components/clusterWizard/disconnected/BasicStep.tsx @@ -0,0 +1,87 @@ +import * as React from 'react'; +import { + ClusterWizardStep, + DeveloperPreview, + ExternalLink, + OCP_RELEASES_PAGE, + StaticTextField, + useTranslation, +} from '../../../../common'; +import { + Switch, + Split, + SplitItem, + TextContent, + Text, + TextVariants, + Grid, + GridItem, + Form, +} from '@patternfly/react-core'; +import { Formik } from 'formik'; +import OcmOpenShiftVersion from '../../clusterConfiguration/OcmOpenShiftVersion'; +import { useClusterWizardContext } from '../ClusterWizardContext'; +import ClusterWizardFooter from '../ClusterWizardFooter'; +import ClusterWizardNavigation from '../ClusterWizardNavigation'; +import { WithErrorBoundary } from '../../../../common/components/ErrorHandling/WithErrorBoundary'; + +const BasicStep = () => { + const { t } = useTranslation(); + const { installDisconnected, setInstallDisconnected, moveNext } = useClusterWizardContext(); + + return ( + { + // nothing to do + }} + > + } + footer={} + > + + + + + + + Basic information + + + + + + + + + setInstallDisconnected(checked)} + ouiaId="DisconnectedInstall" + /> + + +
+ + + + {t('ai:Learn more about OpenShift releases')} + + + + + x86_64 + +
+
+
+
+
+
+ ); +}; + +export default BasicStep; diff --git a/libs/ui-lib/lib/ocm/components/clusterWizard/disconnected/ReviewStep.tsx b/libs/ui-lib/lib/ocm/components/clusterWizard/disconnected/ReviewStep.tsx new file mode 100644 index 0000000000..c5b7abfcf9 --- /dev/null +++ b/libs/ui-lib/lib/ocm/components/clusterWizard/disconnected/ReviewStep.tsx @@ -0,0 +1,123 @@ +import * as React from 'react'; +import ClusterWizardFooter from '../ClusterWizardFooter'; +import { useClusterWizardContext } from '../ClusterWizardContext'; +import { + ClusterWizardStep, + DeveloperPreview, + PULL_SECRET_INFO_LINK, + singleClusterOperators, +} from '../../../../common'; +import ClusterWizardNavigation from '../ClusterWizardNavigation'; +import { WithErrorBoundary } from '../../../../common/components/ErrorHandling/WithErrorBoundary'; +import { + Split, + SplitItem, + Alert, + Grid, + Text, + TextContent, + TextVariants, + DescriptionList, + DescriptionListGroup, + DescriptionListTerm, + DescriptionListDescription, + List, + ListItem, + ListComponent, + OrderType, +} from '@patternfly/react-core'; +import { Formik } from 'formik'; +import { saveAs } from 'file-saver'; +import { useNavigate } from 'react-router-dom-v5-compat'; + +import { getOperatorSpecs } from '../../../../common/components/operators/operatorSpecs'; + +const downloadUrl = + 'https://mirror.openshift.com/pub/cgw/assisted-installer-disconnected/latest/agent-ove.x86_64.iso'; + +const ReviewStep = () => { + const { moveBack } = useClusterWizardContext(); + const opSpecs = getOperatorSpecs(() => undefined); + const navigate = useNavigate(); + + return ( + { + // nothing to do + }} + > + } + footer={ + { + downloadUrl && saveAs(downloadUrl); + navigate('/cluster-list'); + }} + onBack={moveBack} + nextButtonText="Download ISO" + /> + } + > + + + + + + Review and download ISO + + + + + + + + + Download ISO + + Download or copy your pull secret from{' '} + + here + + + + Boot your cluster's machines from this ISO and follow instructions + + + Provide your pull secret inside the installation wizard when requested + + + + + + {singleClusterOperators.map((o) => { + const operator = Object.values(opSpecs) + .flatMap((op) => op) + .find((op) => op.operatorKey === o); + return {operator ? operator.title : o}; + })} + + + + + OpenShift version + 4.19 + + + CPU architecture + x86_64 + + + ISO size + approx. 35GB + + + + + + + ); +}; + +export default ReviewStep; diff --git a/libs/ui-lib/lib/ocm/components/clusterWizard/wizardTransition.ts b/libs/ui-lib/lib/ocm/components/clusterWizard/wizardTransition.ts index f2bdd96978..975858bc91 100644 --- a/libs/ui-lib/lib/ocm/components/clusterWizard/wizardTransition.ts +++ b/libs/ui-lib/lib/ocm/components/clusterWizard/wizardTransition.ts @@ -29,7 +29,9 @@ export type ClusterWizardStepsType = | 'networking' | 'review' | 'custom-manifests' - | 'credentials-download'; + | 'credentials-download' + | 'disconnected-basic' + | 'disconnected-review'; const wizardStepsOrder: ClusterWizardStepsType[] = [ 'cluster-details', @@ -45,6 +47,11 @@ const wizardStepsOrder: ClusterWizardStepsType[] = [ 'review', ]; +export const disconnectedSteps: ClusterWizardStepsType[] = [ + 'disconnected-basic', + 'disconnected-review', +]; + export const ClusterWizardFlowStateNew = 'new'; export type ClusterWizardFlowStateType = Cluster['status'] | typeof ClusterWizardFlowStateNew; @@ -251,6 +258,8 @@ const reviewStepValidationsMap: WizardStepValidationMap = { const credentialsValidationMap = buildEmptyValidationsMap(); const customManifestsValidationsMap = buildEmptyValidationsMap(); +const disconnectedReviewValidationsMap = buildEmptyValidationsMap(); +const disconnectedBasicValidationsMap = buildEmptyValidationsMap(); export const wizardStepsValidationsMap: WizardStepsValidationMap = { 'cluster-details': clusterDetailsStepValidationsMap, @@ -264,6 +273,8 @@ export const wizardStepsValidationsMap: WizardStepsValidationMap