Skip to content

Commit 353b6d0

Browse files
committed
Resolves: MTV-3641 | add customization scripts wizard step
Signed-off-by: Jeff Puzzo <jpuzzo@redhat.com>
1 parent 70e0c52 commit 353b6d0

32 files changed

+1261
-74
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,8 @@ kubectl.sha256
2323

2424
forkliftci
2525

26+
# Local dev fixtures (test data, debug helpers)
27+
.dev/
28+
2629
# Cursor IDE - only ignore sensitive MCP config (prompts are shared)
2730
.cursor/mcp.json

cspell.wordlist.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,5 @@ pfext
110110
nads
111111
cooldown
112112
Lightspeed
113+
firstboot
114+
configmap

locales/en/plugin__forklift-console-plugin.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@
137137
"Add Label to specify qualifying nodes": "Add Label to specify qualifying nodes",
138138
"Add mapping": "Add mapping",
139139
"Add passphrase": "Add passphrase",
140+
"Add script": "Add script",
140141
"Add virtual machines": "Add virtual machines",
141142
"Add virtual machines to migration plan": "Add virtual machines to migration plan",
142143
"Additional setup": "Additional setup",

package-lock.json

Lines changed: 65 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
"@patternfly/patternfly": "6.4.0",
4848
"@patternfly/quickstarts": "6.4.0",
4949
"@patternfly/react-charts": "8.4.0",
50+
"@patternfly/react-code-editor": "^6.4.0",
5051
"@patternfly/react-component-groups": "6.4.0",
5152
"@patternfly/react-core": "6.4.0",
5253
"@patternfly/react-drag-drop": "6.4.0",

src/components/FieldBuilderTable/FieldBuilderTable.style.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222
}
2323
}
2424

25+
// Remove row borders
26+
.pf-v6-c-table__tbody .pf-v6-c-table__tr {
27+
border-block-end: 0;
28+
}
29+
2530
// Align first column cells
2631
.pf-v6-c-table__tbody .pf-v6-c-table__td:first-of-type {
2732
padding-left: 0;

src/components/SdkYamlEditor/SdkYamlEditor.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Base64 } from 'js-base64';
33
import Loading from 'src/components/Loading/Loading';
44

55
import { CodeEditor } from '@openshift-console/dynamic-plugin-sdk';
6+
import { Language } from '@patternfly/react-code-editor';
67
import { Bullseye } from '@patternfly/react-core';
78

89
import './SdkYamlEditor.scss';
@@ -26,13 +27,13 @@ const SdkYamlEditor: FC<SdkYamlEditorProps> = ({ minHeight = '20rem', onChange,
2627
>
2728
<div className="code-editor-container">
2829
<CodeEditor
29-
language="yaml"
30+
language={Language.yaml}
3031
value={decodedValue}
3132
onChange={(val: string) => {
3233
onChange(Base64.encode(val));
3334
}}
3435
minHeight={minHeight}
35-
showMiniMap={false}
36+
isMinimapVisible={false}
3637
/>
3738
</div>
3839
</Suspense>

src/plans/create/CreatePlanWizard.style.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
.plan-create-page {
2+
display: flex;
3+
flex-direction: column;
4+
height: 100%;
5+
}
6+
17
.create-plan-wizard {
28
.pf-v6-c-wizard__main {
39
overflow-x: auto;

src/plans/create/CreatePlanWizardInner.tsx

Lines changed: 12 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import { type FC, useEffect, useRef, useState } from 'react';
1+
import { type FC, useState } from 'react';
22

33
import type { V1beta1Provider } from '@forklift-ui/types';
4-
import { Wizard, type WizardProps, WizardStep, type WizardStepType } from '@patternfly/react-core';
5-
import { TELEMETRY_EVENTS } from '@utils/analytics/constants';
6-
import { useForkliftAnalytics } from '@utils/analytics/hooks/useForkliftAnalytics';
4+
import { Wizard, WizardStep } from '@patternfly/react-core';
75
import { useForkliftTranslation } from '@utils/i18n';
86

9-
import { useStepValidation } from './hooks/useStepValidation';
7+
import { useStepNavigation } from './hooks/useStepNavigation';
8+
import CustomScriptsStep from './steps/customization-scripts/CustomScriptsStep';
109
import GeneralInformationStep from './steps/general-information/GeneralInformationStep';
1110
import HooksStep from './steps/migration-hooks/HooksStep';
1211
import MigrationTypeStep from './steps/migration-type/MigrationTypeStep';
@@ -19,7 +18,7 @@ import VirtualMachinesStepFooter from './steps/virtual-machines/VirtualMachinesS
1918
import { hasLiveMigrationProviderType } from './utils/hasLiveMigrationProviderType';
2019
import { hasPreviousStepErrors } from './utils/hasPreviousStepErrors';
2120
import { hasWarmMigrationProviderType } from './utils/hasWarmMigrationProviderType';
22-
import { firstStep, planStepNames, planStepOrder, PlanWizardStepId } from './constants';
21+
import { planStepNames, planStepOrder, PlanWizardStepId } from './constants';
2322
import CreatePlanWizardFooter from './CreatePlanWizardFooter';
2423

2524
type CreatePlanWizardInnerProps = {
@@ -36,56 +35,11 @@ const CreatePlanWizardInner: FC<CreatePlanWizardInnerProps> = ({
3635
sourceProvider,
3736
}) => {
3837
const { t } = useForkliftTranslation();
39-
const { trackEvent } = useForkliftAnalytics();
40-
const [currentStep, setCurrentStep] = useState<WizardStepType>(firstStep);
4138
const [createPlanError, setCreatePlanError] = useState<Error | undefined>();
42-
const { hasStepErrors, validateStep } = useStepValidation();
43-
const initialStepTracked = useRef(false);
39+
const { currentStep, handleStepChange, hasStepErrors } = useStepNavigation();
4440

4541
const hasCreatePlanError = Boolean(createPlanError?.message);
4642

47-
// Track initial step visit when wizard loads
48-
useEffect(() => {
49-
if (!initialStepTracked.current) {
50-
initialStepTracked.current = true;
51-
trackEvent(TELEMETRY_EVENTS.PLAN_WIZARD_STEP_VISITED, {
52-
stepId: firstStep.id,
53-
});
54-
}
55-
}, [trackEvent]);
56-
57-
const handleStepChange: WizardProps['onStepChange'] = async (_event, newStep) => {
58-
const currentStepId = currentStep.id as PlanWizardStepId;
59-
const newStepId = newStep.id as PlanWizardStepId;
60-
const newStepOrder = planStepOrder[newStepId];
61-
const currentStepOrder = planStepOrder[currentStepId];
62-
63-
if (currentStepId === newStepId) {
64-
return;
65-
}
66-
67-
// Allow backward navigation without validation
68-
if (newStepOrder <= currentStepOrder) {
69-
setCurrentStep(newStep);
70-
trackEvent(TELEMETRY_EVENTS.PLAN_WIZARD_STEP_VISITED, {
71-
stepId: newStepId,
72-
});
73-
return;
74-
}
75-
76-
try {
77-
const isCurrentStepValid = await validateStep(currentStepId);
78-
if (isCurrentStepValid) {
79-
setCurrentStep(newStep);
80-
trackEvent(TELEMETRY_EVENTS.PLAN_WIZARD_STEP_VISITED, {
81-
stepId: newStepId,
82-
});
83-
}
84-
} catch {
85-
// Stay on current step if validation throws
86-
}
87-
};
88-
8943
const handleSubmit = async () => {
9044
setCreatePlanError(undefined);
9145
try {
@@ -163,6 +117,12 @@ const CreatePlanWizardInner: FC<CreatePlanWizardInnerProps> = ({
163117
>
164118
<OtherSettingsStep isLiveMigrationFeatureEnabled={isLiveMigrationFeatureEnabled} />
165119
</WizardStep>,
120+
<WizardStep
121+
key={PlanWizardStepId.CustomizationScripts}
122+
{...getStepProps(PlanWizardStepId.CustomizationScripts)}
123+
>
124+
<CustomScriptsStep />
125+
</WizardStep>,
166126
<WizardStep key={PlanWizardStepId.Hooks} {...getStepProps(PlanWizardStepId.Hooks)}>
167127
<HooksStep />
168128
</WizardStep>,

src/plans/create/PlanCreatePage.tsx

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,22 @@ const PlanCreatePage: FC = () => {
1212

1313
return (
1414
<LearningExperienceDrawer>
15-
<PageSection hasBodyWrapper={false}>
16-
<Split>
17-
<SplitItem isFilled>
18-
<Title headingLevel="h2">{t('Create migration plan')}</Title>
19-
</SplitItem>
20-
<SplitItem>
21-
<LearningExperienceButton />
22-
</SplitItem>
23-
</Split>
24-
</PageSection>
15+
<div className="plan-create-page">
16+
<PageSection hasBodyWrapper={false}>
17+
<Split>
18+
<SplitItem isFilled>
19+
<Title headingLevel="h2">{t('Create migration plan')}</Title>
20+
</SplitItem>
21+
<SplitItem>
22+
<LearningExperienceButton />
23+
</SplitItem>
24+
</Split>
25+
</PageSection>
2526

26-
<PageSection hasBodyWrapper={false} hasOverflowScroll type={PageSectionTypes.wizard}>
27-
<CreatePlanWizard />
28-
</PageSection>
27+
<PageSection hasBodyWrapper={false} type={PageSectionTypes.wizard}>
28+
<CreatePlanWizard />
29+
</PageSection>
30+
</div>
2931
</LearningExperienceDrawer>
3032
);
3133
};

0 commit comments

Comments
 (0)