V2Wizrad: fix validation at file system step (HMS-3733)
this commit fix validation when usr choose duplicate mount point andwhen there is no root partition
This commit is contained in:
parent
92fbb8babf
commit
698d21df42
5 changed files with 117 additions and 4 deletions
|
|
@ -11,6 +11,7 @@ import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||||
|
|
||||||
import DetailsStep from './steps/Details';
|
import DetailsStep from './steps/Details';
|
||||||
import FileSystemStep from './steps/FileSystem';
|
import FileSystemStep from './steps/FileSystem';
|
||||||
|
import { FileSystemStepFooter } from './steps/FileSystem/FileSystemConfiguration';
|
||||||
import ImageOutputStep from './steps/ImageOutput';
|
import ImageOutputStep from './steps/ImageOutput';
|
||||||
import OscapStep from './steps/Oscap';
|
import OscapStep from './steps/Oscap';
|
||||||
import PackagesStep from './steps/Packages';
|
import PackagesStep from './steps/Packages';
|
||||||
|
|
@ -260,7 +261,7 @@ const CreateImageWizard = ({ startStepIndex = 1 }: CreateImageWizardProps) => {
|
||||||
<WizardStep
|
<WizardStep
|
||||||
name="File system configuration"
|
name="File system configuration"
|
||||||
id="step-file-system"
|
id="step-file-system"
|
||||||
footer={<CustomWizardFooter disableNext={false} />}
|
footer={<FileSystemStepFooter />}
|
||||||
>
|
>
|
||||||
<FileSystemStep />
|
<FileSystemStep />
|
||||||
</WizardStep>
|
</WizardStep>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Alert,
|
Alert,
|
||||||
|
|
@ -8,6 +8,8 @@ import {
|
||||||
TextContent,
|
TextContent,
|
||||||
TextInput,
|
TextInput,
|
||||||
TextVariants,
|
TextVariants,
|
||||||
|
useWizardContext,
|
||||||
|
WizardFooterWrapper,
|
||||||
} from '@patternfly/react-core';
|
} from '@patternfly/react-core';
|
||||||
import { Select, SelectOption } from '@patternfly/react-core/deprecated';
|
import { Select, SelectOption } from '@patternfly/react-core/deprecated';
|
||||||
import {
|
import {
|
||||||
|
|
@ -29,9 +31,16 @@ import {
|
||||||
removePartition,
|
removePartition,
|
||||||
selectPartitions,
|
selectPartitions,
|
||||||
changePartitionUnit,
|
changePartitionUnit,
|
||||||
|
setIsNextButtonTouched,
|
||||||
|
selectIsNextButtonTouched,
|
||||||
|
selectFileSystemPartitionMode,
|
||||||
} from '../../../../store/wizardSlice';
|
} from '../../../../store/wizardSlice';
|
||||||
import UsrSubDirectoriesDisabled from '../../UsrSubDirectoriesDisabled';
|
import UsrSubDirectoriesDisabled from '../../UsrSubDirectoriesDisabled';
|
||||||
import { ValidatedTextInput } from '../../ValidatedTextInput';
|
import { ValidatedTextInput } from '../../ValidatedTextInput';
|
||||||
|
import {
|
||||||
|
getDuplicateMountPoints,
|
||||||
|
isFileSystemConfigValid,
|
||||||
|
} from '../../validators';
|
||||||
|
|
||||||
export type Partition = {
|
export type Partition = {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -40,12 +49,58 @@ export type Partition = {
|
||||||
unit: Units;
|
unit: Units;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const FileSystemStepFooter = () => {
|
||||||
|
const { goToNextStep, goToPrevStep, close } = useWizardContext();
|
||||||
|
const [isValid, setIsValid] = useState(false);
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const [isNextDisabled, setNextDisabled] = useState(false);
|
||||||
|
const fileSystemPartitionMode = useAppSelector((state) =>
|
||||||
|
selectFileSystemPartitionMode(state)
|
||||||
|
);
|
||||||
|
const partitions = useAppSelector((state) => selectPartitions(state));
|
||||||
|
|
||||||
|
const onValidate = () => {
|
||||||
|
dispatch(setIsNextButtonTouched(false));
|
||||||
|
if (!isValid) {
|
||||||
|
setNextDisabled(true);
|
||||||
|
} else {
|
||||||
|
goToNextStep();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
fileSystemPartitionMode === 'automatic' ||
|
||||||
|
isFileSystemConfigValid(partitions)
|
||||||
|
) {
|
||||||
|
setIsValid(true);
|
||||||
|
} else setIsValid(false);
|
||||||
|
setNextDisabled(false);
|
||||||
|
dispatch(setIsNextButtonTouched(true));
|
||||||
|
}, [partitions, fileSystemPartitionMode, dispatch]);
|
||||||
|
return (
|
||||||
|
<WizardFooterWrapper>
|
||||||
|
<Button onClick={onValidate} isDisabled={isNextDisabled}>
|
||||||
|
Next
|
||||||
|
</Button>
|
||||||
|
<Button variant="secondary" onClick={goToPrevStep}>
|
||||||
|
Back
|
||||||
|
</Button>
|
||||||
|
<Button ouiaId="wizard-cancel-btn" variant="link" onClick={close}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</WizardFooterWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const FileSystemConfiguration = () => {
|
const FileSystemConfiguration = () => {
|
||||||
const partitions = useAppSelector((state) => selectPartitions(state));
|
const partitions = useAppSelector((state) => selectPartitions(state));
|
||||||
const environments = useAppSelector((state) => selectImageTypes(state));
|
const environments = useAppSelector((state) => selectImageTypes(state));
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const isNextButtonPristine = useAppSelector((state) =>
|
||||||
|
selectIsNextButtonTouched(state)
|
||||||
|
);
|
||||||
const handleAddPartition = () => {
|
const handleAddPartition = () => {
|
||||||
const id = uuidv4();
|
const id = uuidv4();
|
||||||
dispatch(
|
dispatch(
|
||||||
|
|
@ -66,6 +121,17 @@ const FileSystemConfiguration = () => {
|
||||||
{partitions?.find((partition) =>
|
{partitions?.find((partition) =>
|
||||||
partition?.mountpoint?.includes('/usr')
|
partition?.mountpoint?.includes('/usr')
|
||||||
) && <UsrSubDirectoriesDisabled />}
|
) && <UsrSubDirectoriesDisabled />}
|
||||||
|
{!isNextButtonPristine &&
|
||||||
|
getDuplicateMountPoints(partitions)?.length !== 0 &&
|
||||||
|
getDuplicateMountPoints(partitions)?.length !== undefined && (
|
||||||
|
<div>
|
||||||
|
<Alert
|
||||||
|
isInline
|
||||||
|
variant="warning"
|
||||||
|
title="Duplicate mount points: All mount points must be unique. Remove the duplicate or choose a new mount point."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<TextContent>
|
<TextContent>
|
||||||
<Text>
|
<Text>
|
||||||
Create partitions for your image by defining mount points and minimum
|
Create partitions for your image by defining mount points and minimum
|
||||||
|
|
@ -167,16 +233,29 @@ const getSuffix = (mountpoint: string) => {
|
||||||
|
|
||||||
const Row = ({ partition }: RowPropTypes) => {
|
const Row = ({ partition }: RowPropTypes) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
const partitions = useAppSelector((state) => selectPartitions(state));
|
||||||
const handleRemovePartition = (id: string) => {
|
const handleRemovePartition = (id: string) => {
|
||||||
dispatch(removePartition(id));
|
dispatch(removePartition(id));
|
||||||
};
|
};
|
||||||
|
const isNextButtonPristine = useAppSelector((state) =>
|
||||||
|
selectIsNextButtonTouched(state)
|
||||||
|
);
|
||||||
|
const duplicates = getDuplicateMountPoints(partitions);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tr>
|
<Tr>
|
||||||
<Td />
|
<Td />
|
||||||
<Td width={20}>
|
<Td className="pf-m-width-20">
|
||||||
<MountpointPrefix partition={partition} />
|
<MountpointPrefix partition={partition} />
|
||||||
|
{!isNextButtonPristine &&
|
||||||
|
duplicates.indexOf(partition.mountpoint) !== -1 && (
|
||||||
|
<Alert
|
||||||
|
variant="danger"
|
||||||
|
isInline
|
||||||
|
isPlain
|
||||||
|
title="Duplicate mount point."
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Td>
|
</Td>
|
||||||
{partition.mountpoint !== '/' &&
|
{partition.mountpoint !== '/' &&
|
||||||
!partition.mountpoint.startsWith('/boot') &&
|
!partition.mountpoint.startsWith('/boot') &&
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,7 @@ export const mapRequestToState = (request: BlueprintResponse): wizardState => {
|
||||||
fileSystem: {
|
fileSystem: {
|
||||||
mode: 'automatic',
|
mode: 'automatic',
|
||||||
partitions: [],
|
partitions: [],
|
||||||
|
isNextButtonTouched: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
architecture: request.image_requests[0].architecture,
|
architecture: request.image_requests[0].architecture,
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { Partition } from './steps/FileSystem/FileSystemConfiguration';
|
||||||
|
|
||||||
export const isAwsAccountIdValid = (awsAccountId: string | undefined) => {
|
export const isAwsAccountIdValid = (awsAccountId: string | undefined) => {
|
||||||
return (
|
return (
|
||||||
awsAccountId !== undefined &&
|
awsAccountId !== undefined &&
|
||||||
|
|
@ -38,3 +40,23 @@ export const isBlueprintNameValid = (blueprintName: string) =>
|
||||||
export const isBlueprintDescriptionValid = (blueprintDescription: string) => {
|
export const isBlueprintDescriptionValid = (blueprintDescription: string) => {
|
||||||
return blueprintDescription.length <= 250;
|
return blueprintDescription.length <= 250;
|
||||||
};
|
};
|
||||||
|
export const isFileSystemConfigValid = (partitions: Partition[]) => {
|
||||||
|
const duplicates = getDuplicateMountPoints(partitions);
|
||||||
|
return duplicates.length === 0;
|
||||||
|
};
|
||||||
|
export const getDuplicateMountPoints = (partitions: Partition[]): string[] => {
|
||||||
|
const mountPointSet: Set<string> = new Set();
|
||||||
|
const duplicates: string[] = [];
|
||||||
|
if (!partitions) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
for (const partition of partitions) {
|
||||||
|
const mountPoint = partition.mountpoint;
|
||||||
|
if (mountPointSet.has(mountPoint)) {
|
||||||
|
duplicates.push(mountPoint);
|
||||||
|
} else {
|
||||||
|
mountPointSet.add(mountPoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return duplicates;
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ export type wizardState = {
|
||||||
fileSystem: {
|
fileSystem: {
|
||||||
mode: FileSystemPartitionMode;
|
mode: FileSystemPartitionMode;
|
||||||
partitions: Partition[];
|
partitions: Partition[];
|
||||||
|
isNextButtonTouched: boolean;
|
||||||
};
|
};
|
||||||
repositories: {
|
repositories: {
|
||||||
customRepositories: CustomRepository[];
|
customRepositories: CustomRepository[];
|
||||||
|
|
@ -130,6 +131,7 @@ const initialState: wizardState = {
|
||||||
fileSystem: {
|
fileSystem: {
|
||||||
mode: 'automatic',
|
mode: 'automatic',
|
||||||
partitions: [],
|
partitions: [],
|
||||||
|
isNextButtonTouched: true,
|
||||||
},
|
},
|
||||||
repositories: {
|
repositories: {
|
||||||
customRepositories: [],
|
customRepositories: [],
|
||||||
|
|
@ -233,6 +235,9 @@ export const selectEnabledServices = (state: RootState) => {
|
||||||
export const selectFileSystemPartitionMode = (state: RootState) => {
|
export const selectFileSystemPartitionMode = (state: RootState) => {
|
||||||
return state.wizard.fileSystem.mode;
|
return state.wizard.fileSystem.mode;
|
||||||
};
|
};
|
||||||
|
export const selectIsNextButtonTouched = (state: RootState) => {
|
||||||
|
return state.wizard.fileSystem.isNextButtonTouched;
|
||||||
|
};
|
||||||
|
|
||||||
export const selectPartitions = (state: RootState) => {
|
export const selectPartitions = (state: RootState) => {
|
||||||
return state.wizard.fileSystem.partitions;
|
return state.wizard.fileSystem.partitions;
|
||||||
|
|
@ -379,6 +384,10 @@ export const wizardSlice = createSlice({
|
||||||
) => {
|
) => {
|
||||||
state.fileSystem.partitions = action.payload;
|
state.fileSystem.partitions = action.payload;
|
||||||
},
|
},
|
||||||
|
setIsNextButtonTouched: (state, action: PayloadAction<boolean>) => {
|
||||||
|
state.fileSystem.isNextButtonTouched = action.payload;
|
||||||
|
},
|
||||||
|
|
||||||
changeFileSystemPartitionMode: (
|
changeFileSystemPartitionMode: (
|
||||||
state,
|
state,
|
||||||
action: PayloadAction<FileSystemPartitionMode>
|
action: PayloadAction<FileSystemPartitionMode>
|
||||||
|
|
@ -489,6 +498,7 @@ export const {
|
||||||
changeDisabledServices,
|
changeDisabledServices,
|
||||||
changeEnabledServices,
|
changeEnabledServices,
|
||||||
changeFileSystemConfiguration,
|
changeFileSystemConfiguration,
|
||||||
|
setIsNextButtonTouched,
|
||||||
changeFileSystemPartitionMode,
|
changeFileSystemPartitionMode,
|
||||||
addPartition,
|
addPartition,
|
||||||
removePartition,
|
removePartition,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue