Skip to content

Commit 2a36974

Browse files
authored
feat(web): add initial upgrade flow (#2955)
* feat(web): add initial upgrade flow * chore: address linting issues * chore: cleanup console.log * chore: change the default back
1 parent 47d23f0 commit 2a36974

31 files changed

+1340
-141
lines changed

web/src/components/wizard/InstallWizard.tsx

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import ConfigurationStep from "./config/ConfigurationStep";
55
import LinuxSetupStep from "./setup/LinuxSetupStep";
66
import KubernetesSetupStep from "./setup/KubernetesSetupStep";
77
import InstallationStep from "./installation/InstallationStep";
8+
import UpgradeStep from "./installation/UpgradeStep";
89
import LinuxCompletionStep from "./completion/LinuxCompletionStep";
910
import KubernetesCompletionStep from "./completion/KubernetesCompletionStep";
1011
import { WizardStep } from "../../types";
@@ -13,26 +14,28 @@ import { useWizard } from "../../contexts/WizardModeContext";
1314

1415
const InstallWizard: React.FC = () => {
1516
const [currentStep, setCurrentStep] = useState<WizardStep>("welcome");
16-
const { text, target } = useWizard();
17+
const { text, target, mode } = useWizard();
18+
let steps: WizardStep[] = []
1719

18-
const getSteps = (): WizardStep[] => {
19-
if (target === "kubernetes") {
20-
return ["welcome", "configuration", "kubernetes-setup", "installation", "kubernetes-completion"];
21-
} else {
22-
return ["welcome", "configuration", "linux-setup", "installation", "linux-completion"];
23-
}
20+
// TODO Upgrade
21+
// Iteration 1:
22+
// - Remove configuration step for upgrades for now
23+
// - There's no setup step for upgrades
24+
if (mode == "upgrade") {
25+
steps = ["welcome", "installation", `${target}-completion`]
26+
} else {
27+
// install steps
28+
steps = ["welcome", "configuration", `${target}-setup`, "installation", `${target}-completion`]
2429
}
2530

2631
const goToNextStep = () => {
27-
const steps = getSteps();
2832
const currentIndex = steps.indexOf(currentStep);
2933
if (currentIndex < steps.length - 1) {
3034
setCurrentStep(steps[currentIndex + 1]);
3135
}
3236
};
3337

3438
const goToPreviousStep = () => {
35-
const steps = getSteps();
3639
const currentIndex = steps.indexOf(currentStep);
3740
if (currentIndex > 0) {
3841
setCurrentStep(steps[currentIndex - 1]);
@@ -50,6 +53,10 @@ const InstallWizard: React.FC = () => {
5053
case "kubernetes-setup":
5154
return <KubernetesSetupStep onNext={goToNextStep} onBack={goToPreviousStep} />;
5255
case "installation":
56+
// TODO Upgrade use a dedicated upgrade component while we work on making the upgrade flow similar to installation
57+
if (mode == "upgrade") {
58+
return <UpgradeStep onNext={goToNextStep} onBack={goToPreviousStep} />;
59+
}
5360
return <InstallationStep onNext={goToNextStep} onBack={goToPreviousStep} />;
5461
case "linux-completion":
5562
return <LinuxCompletionStep />;
@@ -80,7 +87,7 @@ const InstallWizard: React.FC = () => {
8087

8188
<main className="grow">
8289
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
83-
<StepNavigation currentStep={currentStep} />
90+
<StepNavigation currentStep={currentStep} enabledSteps={steps} />
8491
<div className="mt-8">{renderStep()}</div>
8592
</div>
8693
</main>

web/src/components/wizard/StepNavigation.tsx

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import React from 'react';
22
import { WizardStep } from '../../types';
3+
import { WizardMode } from '../../types/wizard-mode.ts';
34
import { ClipboardList, Settings, Download, CheckCircle, Server } from 'lucide-react';
45
import { useWizard } from '../../contexts/WizardModeContext';
56
import { useSettings } from '../../contexts/SettingsContext';
67

78
interface StepNavigationProps {
89
currentStep: WizardStep;
10+
enabledSteps: WizardStep[];
911
}
1012

1113
interface NavigationStep {
@@ -16,32 +18,28 @@ interface NavigationStep {
1618
parentId?: WizardStep;
1719
}
1820

19-
const StepNavigation: React.FC<StepNavigationProps> = ({ currentStep: currentStepId }) => {
20-
const { mode, target } = useWizard();
21+
const getNavigationSteps = (mode: WizardMode): NavigationStep[] => {
22+
return [
23+
{ id: 'welcome', name: 'Welcome', icon: ClipboardList },
24+
{ id: 'configuration', name: 'Configuration', icon: Server },
25+
{ id: 'linux-setup', name: 'Setup', icon: Settings },
26+
{ id: 'kubernetes-setup', name: 'Setup', icon: Settings },
27+
{ id: 'installation', name: mode === 'upgrade' ? 'Upgrade' : 'Installation', icon: Download },
28+
{ id: 'kubernetes-completion', name: 'Completion', icon: CheckCircle },
29+
{ id: 'linux-completion', name: 'Completion', icon: CheckCircle },
30+
]
31+
}
32+
33+
const StepNavigation: React.FC<StepNavigationProps> = ({ currentStep: currentStepId, enabledSteps }) => {
34+
const { mode } = useWizard();
2135
const { settings } = useSettings();
2236
const themeColor = settings.themeColor;
2337

24-
const getSteps = (): NavigationStep[] => {
25-
if (target === 'kubernetes') {
26-
return [
27-
{ id: 'welcome', name: 'Welcome', icon: ClipboardList },
28-
{ id: 'configuration', name: 'Configuration', icon: Server },
29-
{ id: 'kubernetes-setup', name: 'Setup', icon: Settings },
30-
{ id: 'installation', name: mode === 'upgrade' ? 'Upgrade' : 'Installation', icon: Download },
31-
{ id: 'kubernetes-completion', name: 'Completion', icon: CheckCircle },
32-
];
33-
} else {
34-
return [
35-
{ id: 'welcome', name: 'Welcome', icon: ClipboardList },
36-
{ id: 'configuration', name: 'Configuration', icon: Server },
37-
{ id: 'linux-setup', name: 'Setup', icon: Settings },
38-
{ id: 'installation', name: mode === 'upgrade' ? 'Upgrade' : 'Installation', icon: Download },
39-
{ id: 'linux-completion', name: 'Completion', icon: CheckCircle },
40-
];
41-
}
42-
}
38+
// Get the navigation steps for this wizard mode and then filter them, removing steps the wizard isn't
39+
// configured to use
40+
const steps = getNavigationSteps(mode)
41+
.filter(({ id: navId }) => enabledSteps.includes(navId));
4342

44-
const steps = getSteps();
4543
const currentStep = steps.find(step => step.id === currentStepId);
4644

4745
const getStepStatus = (step: NavigationStep) => {
@@ -89,4 +87,4 @@ const StepNavigation: React.FC<StepNavigationProps> = ({ currentStep: currentSte
8987
);
9088
};
9189

92-
export default StepNavigation;
90+
export default StepNavigation;

web/src/components/wizard/config/ConfigurationStep.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { ChevronRight, Loader2 } from 'lucide-react';
1616
import { handleUnauthorized } from '../../../utils/auth';
1717
import { useDebouncedFetch } from '../../../utils/debouncedFetch';
1818
import { AppConfig, AppConfigGroup, AppConfigItem, AppConfigValues } from '../../../types';
19+
import { getApiBase } from '../../../utils/api-base';
1920

2021

2122
interface ConfigurationStepProps {
@@ -27,7 +28,7 @@ interface ConfigError extends Error {
2728
}
2829

2930
const ConfigurationStep: React.FC<ConfigurationStepProps> = ({ onNext }) => {
30-
const { text, target } = useWizard();
31+
const { text, target, mode } = useWizard();
3132
const { token } = useAuth();
3233
const { settings } = useSettings();
3334
const [activeTab, setActiveTab] = useState<string>('');
@@ -157,7 +158,8 @@ const ConfigurationStep: React.FC<ConfigurationStepProps> = ({ onNext }) => {
157158
// Mutation to save config values
158159
const { mutate: submitConfigValues } = useMutation<void, ConfigError>({
159160
mutationFn: async () => {
160-
const response = await fetch(`/api/${target}/install/app/config/values`, {
161+
const apiBase = getApiBase(target, mode);
162+
const response = await fetch(`${apiBase}/app/config/values`, {
161163
method: 'PATCH',
162164
headers: {
163165
'Content-Type': 'application/json',

web/src/components/wizard/installation/InstallationTimeline.tsx

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -44,41 +44,38 @@ const InstallationTimeline: React.FC<InstallationTimelineProps> = ({
4444
return (
4545
<div className="w-80 bg-gray-50 border-r border-gray-200 p-6">
4646
<h3 className="text-lg font-medium text-gray-900 mb-6" data-testid="timeline-title">Installation Progress</h3>
47-
47+
4848
<div className="space-y-6">
4949
{phaseOrder.map((phaseKey) => {
5050
const phase = phases[phaseKey];
5151
const isActive = currentPhase === phaseKey;
5252
const isSelected = selectedPhase === phaseKey;
5353
const isFailed = phase.status === 'Failed';
5454
const isClickable = phase.status !== 'Pending';
55-
55+
5656
return (
5757
<div key={phaseKey} className="relative">
5858
<button
59-
className={`flex items-start space-x-3 text-left w-full p-2 rounded-md transition-colors ${
60-
isClickable ? 'hover:bg-gray-100 cursor-pointer' : 'cursor-default'
61-
} ${isSelected ? 'bg-blue-50 border border-blue-200' : ''}`}
59+
className={`flex items-start space-x-3 text-left w-full p-2 rounded-md transition-colors ${isClickable ? 'hover:bg-gray-100 cursor-pointer' : 'cursor-default'
60+
} ${isSelected ? 'bg-blue-50 border border-blue-200' : ''}`}
6261
onClick={() => isClickable && onPhaseClick(phaseKey)}
6362
disabled={!isClickable}
6463
data-testid={`timeline-${phaseKey}`}
6564
>
6665
<div className="shrink-0 mt-0.5">
6766
{getStatusIcon(phase.status)}
6867
</div>
69-
68+
7069
<div className="grow min-w-0">
71-
<h4 className={`text-sm font-medium ${
72-
isSelected ? 'text-gray-900' : isActive ? 'text-gray-900' : phase.status === 'Succeeded' ? 'text-gray-700' : 'text-gray-600'
73-
}`}>
70+
<h4 className={`text-sm font-medium ${isSelected ? 'text-gray-900' : isActive ? 'text-gray-900' : phase.status === 'Succeeded' ? 'text-gray-700' : 'text-gray-600'
71+
}`}>
7472
{phase.title}
7573
</h4>
76-
<p className={`text-xs mt-1 ${
77-
isSelected ? 'text-gray-600' : isActive ? 'text-gray-600' : 'text-gray-500'
78-
}`}>
74+
<p className={`text-xs mt-1 ${isSelected ? 'text-gray-600' : isActive ? 'text-gray-600' : 'text-gray-500'
75+
}`}>
7976
{phase.description}
8077
</p>
81-
78+
8279
{isFailed && phase.error && (
8380
<p className="text-xs text-red-600 mt-1">{phase.error}</p>
8481
)}
@@ -92,4 +89,4 @@ const InstallationTimeline: React.FC<InstallationTimelineProps> = ({
9289
);
9390
};
9491

95-
export default InstallationTimeline;
92+
export default InstallationTimeline;

0 commit comments

Comments
 (0)