Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions src/Components/CreateImageWizard/CreateImageWizard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ import {
useTimezoneValidation,
useUserGroupsValidation,
useUsersValidation,
WIZARD_STEP_IDS,
} from '../CreateImageWizard/utilities/useValidation';

const CreateImageWizard = () => {
Expand Down Expand Up @@ -358,7 +359,9 @@ const CreateImageWizard = () => {
useEffect(() => {
if (!isOnPremise && showWizardModal && !hasTrackedInitialStepRef.current) {
const initialStepId =
mode === 'edit' ? 'review-step' : 'base-settings-step';
mode === 'edit'
? WIZARD_STEP_IDS.REVIEW
: WIZARD_STEP_IDS.BASE_SETTINGS;
const accountId = userData?.identity.internal?.account_id;

analytics.track(`${AMPLITUDE_MODULE_NAME} - Step Viewed`, {
Expand Down Expand Up @@ -415,15 +418,15 @@ const CreateImageWizard = () => {
) => {
const status = (step.id !== activeStep.id && step.status) || 'default';

const isBaseSettingsStep = step.id === 'base-settings-step';
const isBaseSettingsStep = step.id === WIZARD_STEP_IDS.BASE_SETTINGS;
const hasVisitedBaseSettings = _steps.find(
(s) => s.id === 'base-settings-step',
(s) => s.id === WIZARD_STEP_IDS.BASE_SETTINGS,
)?.isVisited;
const canNavigate =
mode === 'edit' ||
step.isVisited ||
isBaseSettingsStep ||
(hasVisitedBaseSettings && !baseSettingsHasErrors);
hasVisitedBaseSettings;

return (
<WizardNavItem
Expand Down Expand Up @@ -484,7 +487,7 @@ const CreateImageWizard = () => {
>
<WizardStep
name='Base settings'
id='base-settings-step'
id={WIZARD_STEP_IDS.BASE_SETTINGS}
navItem={CustomStatusNavItem}
status={baseSettingsHasErrors ? 'error' : 'default'}
footer={
Expand Down Expand Up @@ -540,7 +543,7 @@ const CreateImageWizard = () => {
</WizardStep>
<WizardStep
name='Repositories and packages'
id='content-step'
id={WIZARD_STEP_IDS.CONTENT}
navItem={CustomStatusNavItem}
status='default'
isHidden={
Expand Down Expand Up @@ -571,7 +574,7 @@ const CreateImageWizard = () => {
</WizardStep>
<WizardStep
name='Advanced settings'
id='advanced-settings-step'
id={WIZARD_STEP_IDS.ADVANCED_SETTINGS}
navItem={CustomStatusNavItem}
status={advancedSettingsHasErrors ? 'error' : 'default'}
isHidden={
Expand Down Expand Up @@ -650,7 +653,7 @@ const CreateImageWizard = () => {
</WizardStep>
<WizardStep
name='Review'
id='review-step'
id={WIZARD_STEP_IDS.REVIEW}
navItem={CustomStatusNavItem}
status='default'
footer={<ReviewWizardFooter />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
} from '@/store/slices/wizard';

import { scrollToFirstError } from '../utilities/scrollToFirstError';
import { WIZARD_STEP_IDS } from '../utilities/useValidation';

type CustomWizardFooterPropType = {
disableBack?: boolean;
Expand Down Expand Up @@ -78,7 +79,7 @@ export const CustomWizardFooter = ({
});
}
dispatch(resetForceShowErrors());
goToStepById('review-step');
goToStepById(WIZARD_STEP_IDS.REVIEW);
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
WizardFooterWrapper,
} from '@patternfly/react-core';
import { MenuToggleElement } from '@patternfly/react-core/dist/esm/components/MenuToggle/MenuToggle';
import { flushSync } from 'react-dom';

import { selectSelectedBlueprintId } from '@/store/slices/blueprint';
import { selectWizardModalMode } from '@/store/slices/wizardModal';
Expand All @@ -17,7 +18,11 @@ import {
useCreateBPWithNotification as useCreateBlueprintMutation,
useUpdateBPWithNotification as useUpdateBlueprintMutation,
} from '../../../Hooks';
import { useAppSelector } from '../../../store/hooks';
import { useAppDispatch, useAppSelector } from '../../../store/hooks';
import {
resetForceShowErrors,
setForceShowErrors,
} from '../../../store/slices/wizard';
import {
CreateSaveAndBuildBtn,
CreateSaveButton,
Expand All @@ -26,10 +31,12 @@ import {
EditSaveAndBuildBtn,
EditSaveButton,
} from '../steps/Review/Footer/EditDropdown';
import { useIsBlueprintValid } from '../utilities/useValidation';
import { scrollToFirstError } from '../utilities/scrollToFirstError';
import { useBlueprintValidation } from '../utilities/useValidation';

const ReviewWizardFooter = () => {
const { goToPrevStep, close } = useWizardContext();
const { goToPrevStep, goToStepById, close } = useWizardContext();
const dispatch = useAppDispatch();
const { isSuccess: isCreateSuccess, reset: resetCreate } =
useCreateBlueprintMutation({ fixedCacheKey: 'createBlueprintKey' });

Expand All @@ -38,10 +45,26 @@ const ReviewWizardFooter = () => {
const mode = useAppSelector(selectWizardModalMode);
const blueprintId = useAppSelector(selectSelectedBlueprintId);
const [isOpen, setIsOpen] = useState(false);
const { isValid, firstErrorStepId } = useBlueprintValidation();

const handleValidationFail = () => {
if (!firstErrorStepId) return;
flushSync(() => {
dispatch(setForceShowErrors());
});
goToStepById(firstErrorStepId);
requestAnimationFrame(() => {
scrollToFirstError();
});
};

const onToggleClick = () => {
if (!isValid) {
handleValidationFail();
return;
}
setIsOpen(!isOpen);
};
const isValid = useIsBlueprintValid();

useEffect(() => {
if (isUpdateSuccess || isCreateSuccess) {
Expand All @@ -51,6 +74,14 @@ const ReviewWizardFooter = () => {
}
}, [isUpdateSuccess, isCreateSuccess, resetCreate, resetUpdate, close]);

const validateBeforeAction = (): boolean => {
if (!isValid) {
handleValidationFail();
return false;
}
return true;
};

const isEditMode = mode === 'edit';

return (
Expand All @@ -59,7 +90,13 @@ const ReviewWizardFooter = () => {
columnGap={{ default: 'columnGapSm' }}
justifyContent={{ default: 'justifyContentFlexEnd' }}
>
<Button variant='secondary' onClick={goToPrevStep}>
<Button
variant='secondary'
onClick={() => {
dispatch(resetForceShowErrors());
goToPrevStep();
}}
>
Back
</Button>
<Dropdown
Expand All @@ -71,7 +108,6 @@ const ReviewWizardFooter = () => {
ref={toggleRef}
onClick={onToggleClick}
isExpanded={isOpen}
isDisabled={!isValid}
splitButtonItems={
isEditMode
? [
Expand All @@ -80,13 +116,15 @@ const ReviewWizardFooter = () => {
setIsOpen={setIsOpen}
blueprintId={blueprintId || ''}
isDisabled={!isValid}
validateBeforeAction={validateBeforeAction}
/>,
]
: [
<CreateSaveButton
key='wizard-create-save-btn'
setIsOpen={setIsOpen}
isDisabled={!isValid}
validateBeforeAction={validateBeforeAction}
/>,
]
}
Expand All @@ -99,11 +137,13 @@ const ReviewWizardFooter = () => {
blueprintId={blueprintId || ''}
setIsOpen={setIsOpen}
isDisabled={!isValid}
validateBeforeAction={validateBeforeAction}
/>
) : (
<CreateSaveAndBuildBtn
setIsOpen={setIsOpen}
isDisabled={!isValid}
validateBeforeAction={validateBeforeAction}
/>
)}
</Dropdown>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import { setBlueprintId } from '@/store/slices/blueprint';
import { selectIsOnPremise } from '@/store/slices/env';
import { mapStateToRequest, selectPackages } from '@/store/slices/wizard';

import { shouldDisableAction } from './shouldDisableAction';

import { AMPLITUDE_MODULE_NAME } from '../../../../../constants';
import {
useComposeBPWithNotification as useComposeBlueprintMutation,
Expand All @@ -41,11 +43,13 @@ import { createAnalytics } from '../../../../../Utilities/analytics';
type CreateDropdownProps = {
setIsOpen: (isOpen: boolean) => void;
isDisabled: boolean;
validateBeforeAction?: () => boolean;
};

export const CreateSaveAndBuildBtn = ({
setIsOpen,
isDisabled,
validateBeforeAction,
}: CreateDropdownProps) => {
const { analytics, auth, isBeta } = useChrome();
const { userData } = useGetUser(auth);
Expand All @@ -59,7 +63,10 @@ export const CreateSaveAndBuildBtn = ({
fixedCacheKey: 'createBlueprintKey',
});
const dispatch = useAppDispatch();
const shouldDisable = shouldDisableAction(isDisabled, validateBeforeAction);

const onSaveAndBuild = async () => {
if (validateBeforeAction && !validateBeforeAction()) return;
const requestBody = mapStateToRequest(store.getState());
setIsOpen(false);

Expand Down Expand Up @@ -92,7 +99,7 @@ export const CreateSaveAndBuildBtn = ({

return (
<DropdownList>
<DropdownItem onClick={onSaveAndBuild} isDisabled={isDisabled}>
<DropdownItem onClick={onSaveAndBuild} isDisabled={shouldDisable}>
Create blueprint and build image(s)
</DropdownItem>
</DropdownList>
Expand Down Expand Up @@ -133,6 +140,7 @@ const SaveAndBuildImagesModal = ({
export const CreateSaveButton = ({
setIsOpen,
isDisabled,
validateBeforeAction,
}: CreateDropdownProps) => {
const { analytics, auth, isBeta } = useChrome();
const { userData } = useGetUser(auth);
Expand All @@ -154,7 +162,10 @@ export const CreateSaveButton = ({
setShowModal(false);
};

const shouldDisable = shouldDisableAction(isDisabled, validateBeforeAction);

const onClick = () => {
if (validateBeforeAction && !validateBeforeAction()) return;
if (!wasModalSeen) {
setShowModal(true);
window.localStorage.setItem('imageBuilder.saveAndBuildModalSeen', 'true');
Expand Down Expand Up @@ -194,7 +205,7 @@ export const CreateSaveButton = ({
<MenuToggleAction
onClick={onClick}
id='wizard-create-save-btn'
isDisabled={isDisabled}
isDisabled={shouldDisable}
>
<Flex display={{ default: 'inlineFlex' }}>
{isLoading && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import {
import { selectIsOnPremise } from '@/store/slices/env';
import { mapStateToRequest, selectPackages } from '@/store/slices/wizard';

import { shouldDisableAction } from './shouldDisableAction';

import { AMPLITUDE_MODULE_NAME } from '../../../../../constants';
import {
useComposeBPWithNotification as useComposeBlueprintMutation,
Expand All @@ -32,12 +34,14 @@ type EditDropdownProps = {
setIsOpen: (isOpen: boolean) => void;
blueprintId: string;
isDisabled: boolean;
validateBeforeAction?: () => boolean;
};

export const EditSaveAndBuildBtn = ({
setIsOpen,
blueprintId,
isDisabled,
validateBeforeAction,
}: EditDropdownProps) => {
const { analytics, auth, isBeta } = useChrome();
const { userData } = useGetUser(auth);
Expand All @@ -51,7 +55,10 @@ export const EditSaveAndBuildBtn = ({
fixedCacheKey: 'updateBlueprintKey',
});

const shouldDisable = shouldDisableAction(isDisabled, validateBeforeAction);

const onSaveAndBuild = async () => {
if (validateBeforeAction && !validateBeforeAction()) return;
const requestBody = mapStateToRequest(store.getState());

if (!isOnPremise) {
Expand Down Expand Up @@ -83,7 +90,7 @@ export const EditSaveAndBuildBtn = ({

return (
<DropdownList>
<DropdownItem onClick={onSaveAndBuild} isDisabled={isDisabled}>
<DropdownItem onClick={onSaveAndBuild} isDisabled={shouldDisable}>
Save changes and build image(s)
</DropdownItem>
</DropdownList>
Expand All @@ -94,6 +101,7 @@ export const EditSaveButton = ({
setIsOpen,
blueprintId,
isDisabled,
validateBeforeAction,
}: EditDropdownProps) => {
const { analytics, auth, isBeta } = useChrome();
const { userData } = useGetUser(auth);
Expand All @@ -105,7 +113,10 @@ export const EditSaveButton = ({
const { trigger: updateBlueprint, isLoading } = useUpdateBlueprintMutation({
fixedCacheKey: 'updateBlueprintKey',
});
const shouldDisable = shouldDisableAction(isDisabled, validateBeforeAction);

const onSave = async () => {
if (validateBeforeAction && !validateBeforeAction()) return;
const requestBody = mapStateToRequest(store.getState());

if (!isOnPremise) {
Expand All @@ -130,7 +141,7 @@ export const EditSaveButton = ({
<MenuToggleAction
onClick={onSave}
id='wizard-edit-save-btn'
isDisabled={isDisabled}
isDisabled={shouldDisable}
>
<Flex display={{ default: 'inlineFlex' }}>
{isLoading && (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// When validateBeforeAction is provided, buttons stay enabled and
// validation runs on click; otherwise fall back to the static flag.
export const shouldDisableAction = (
isDisabled: boolean,
validateBeforeAction?: () => boolean,
): boolean => !validateBeforeAction && isDisabled;
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import {
useWizardContext,
} from '@patternfly/react-core';

import { WizardStepId } from '@/Components/CreateImageWizard/utilities/useValidation';

export const ReviewCardHeader = ({
title,
stepId,
sectionId,
}: {
title: string;
stepId: string;
stepId: WizardStepId;
sectionId?: string;
}) => {
const { goToStepById } = useWizardContext();
Expand Down
Loading
Loading