diff --git a/src/Components/CreateImageWizardV2/CreateImageWizard.tsx b/src/Components/CreateImageWizardV2/CreateImageWizard.tsx
index 839c0c0f..bba7cf54 100644
--- a/src/Components/CreateImageWizardV2/CreateImageWizard.tsx
+++ b/src/Components/CreateImageWizardV2/CreateImageWizard.tsx
@@ -11,6 +11,7 @@ import { useNavigate, useSearchParams } from 'react-router-dom';
import DetailsStep from './steps/Details';
import FileSystemStep from './steps/FileSystem';
+import { FileSystemStepFooter } from './steps/FileSystem/FileSystemConfiguration';
import ImageOutputStep from './steps/ImageOutput';
import OscapStep from './steps/Oscap';
import PackagesStep from './steps/Packages';
@@ -260,7 +261,7 @@ const CreateImageWizard = ({ startStepIndex = 1 }: CreateImageWizardProps) => {
}
+ footer={}
>
diff --git a/src/Components/CreateImageWizardV2/steps/FileSystem/FileSystemConfiguration.tsx b/src/Components/CreateImageWizardV2/steps/FileSystem/FileSystemConfiguration.tsx
index b9fac99c..a8874a9f 100644
--- a/src/Components/CreateImageWizardV2/steps/FileSystem/FileSystemConfiguration.tsx
+++ b/src/Components/CreateImageWizardV2/steps/FileSystem/FileSystemConfiguration.tsx
@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React, { useEffect, useState } from 'react';
import {
Alert,
@@ -8,6 +8,8 @@ import {
TextContent,
TextInput,
TextVariants,
+ useWizardContext,
+ WizardFooterWrapper,
} from '@patternfly/react-core';
import { Select, SelectOption } from '@patternfly/react-core/deprecated';
import {
@@ -29,9 +31,16 @@ import {
removePartition,
selectPartitions,
changePartitionUnit,
+ setIsNextButtonTouched,
+ selectIsNextButtonTouched,
+ selectFileSystemPartitionMode,
} from '../../../../store/wizardSlice';
import UsrSubDirectoriesDisabled from '../../UsrSubDirectoriesDisabled';
import { ValidatedTextInput } from '../../ValidatedTextInput';
+import {
+ getDuplicateMountPoints,
+ isFileSystemConfigValid,
+} from '../../validators';
export type Partition = {
id: string;
@@ -40,12 +49,58 @@ export type Partition = {
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 (
+
+
+
+
+
+ );
+};
+
const FileSystemConfiguration = () => {
const partitions = useAppSelector((state) => selectPartitions(state));
const environments = useAppSelector((state) => selectImageTypes(state));
const dispatch = useAppDispatch();
+ const isNextButtonPristine = useAppSelector((state) =>
+ selectIsNextButtonTouched(state)
+ );
const handleAddPartition = () => {
const id = uuidv4();
dispatch(
@@ -66,6 +121,17 @@ const FileSystemConfiguration = () => {
{partitions?.find((partition) =>
partition?.mountpoint?.includes('/usr')
) && }
+ {!isNextButtonPristine &&
+ getDuplicateMountPoints(partitions)?.length !== 0 &&
+ getDuplicateMountPoints(partitions)?.length !== undefined && (
+
+ )}
Create partitions for your image by defining mount points and minimum
@@ -167,16 +233,29 @@ const getSuffix = (mountpoint: string) => {
const Row = ({ partition }: RowPropTypes) => {
const dispatch = useAppDispatch();
-
+ const partitions = useAppSelector((state) => selectPartitions(state));
const handleRemovePartition = (id: string) => {
dispatch(removePartition(id));
};
+ const isNextButtonPristine = useAppSelector((state) =>
+ selectIsNextButtonTouched(state)
+ );
+ const duplicates = getDuplicateMountPoints(partitions);
return (
|
-
+ |
+ {!isNextButtonPristine &&
+ duplicates.indexOf(partition.mountpoint) !== -1 && (
+
+ )}
|
{partition.mountpoint !== '/' &&
!partition.mountpoint.startsWith('/boot') &&
diff --git a/src/Components/CreateImageWizardV2/utilities/requestMapper.tsx b/src/Components/CreateImageWizardV2/utilities/requestMapper.tsx
index a208ef28..6a34239c 100644
--- a/src/Components/CreateImageWizardV2/utilities/requestMapper.tsx
+++ b/src/Components/CreateImageWizardV2/utilities/requestMapper.tsx
@@ -111,6 +111,7 @@ export const mapRequestToState = (request: BlueprintResponse): wizardState => {
fileSystem: {
mode: 'automatic',
partitions: [],
+ isNextButtonTouched: true,
},
architecture: request.image_requests[0].architecture,
diff --git a/src/Components/CreateImageWizardV2/validators.ts b/src/Components/CreateImageWizardV2/validators.ts
index 7f89f584..bf3b53df 100644
--- a/src/Components/CreateImageWizardV2/validators.ts
+++ b/src/Components/CreateImageWizardV2/validators.ts
@@ -1,3 +1,5 @@
+import { Partition } from './steps/FileSystem/FileSystemConfiguration';
+
export const isAwsAccountIdValid = (awsAccountId: string | undefined) => {
return (
awsAccountId !== undefined &&
@@ -38,3 +40,23 @@ export const isBlueprintNameValid = (blueprintName: string) =>
export const isBlueprintDescriptionValid = (blueprintDescription: string) => {
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 = 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;
+};
diff --git a/src/store/wizardSlice.ts b/src/store/wizardSlice.ts
index bc31a773..3baab078 100644
--- a/src/store/wizardSlice.ts
+++ b/src/store/wizardSlice.ts
@@ -76,6 +76,7 @@ export type wizardState = {
fileSystem: {
mode: FileSystemPartitionMode;
partitions: Partition[];
+ isNextButtonTouched: boolean;
};
repositories: {
customRepositories: CustomRepository[];
@@ -130,6 +131,7 @@ const initialState: wizardState = {
fileSystem: {
mode: 'automatic',
partitions: [],
+ isNextButtonTouched: true,
},
repositories: {
customRepositories: [],
@@ -233,6 +235,9 @@ export const selectEnabledServices = (state: RootState) => {
export const selectFileSystemPartitionMode = (state: RootState) => {
return state.wizard.fileSystem.mode;
};
+export const selectIsNextButtonTouched = (state: RootState) => {
+ return state.wizard.fileSystem.isNextButtonTouched;
+};
export const selectPartitions = (state: RootState) => {
return state.wizard.fileSystem.partitions;
@@ -379,6 +384,10 @@ export const wizardSlice = createSlice({
) => {
state.fileSystem.partitions = action.payload;
},
+ setIsNextButtonTouched: (state, action: PayloadAction) => {
+ state.fileSystem.isNextButtonTouched = action.payload;
+ },
+
changeFileSystemPartitionMode: (
state,
action: PayloadAction
@@ -489,6 +498,7 @@ export const {
changeDisabledServices,
changeEnabledServices,
changeFileSystemConfiguration,
+ setIsNextButtonTouched,
changeFileSystemPartitionMode,
addPartition,
removePartition,