Skip to content

Commit 6bc92e8

Browse files
authored
chore(web): consistent usage of the intial state context (#2460)
* chore(web): consistent usage of the intial state context * chore: make linter happy
1 parent a6531c2 commit 6bc92e8

15 files changed

+397
-136
lines changed

web/package-lock.json

Lines changed: 0 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

web/src/App.tsx

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { LinuxConfigProvider } from "./contexts/LinuxConfigContext";
33
import { KubernetesConfigProvider } from "./contexts/KubernetesConfigContext";
44
import { SettingsProvider } from "./contexts/SettingsContext";
55
import { WizardProvider } from "./contexts/WizardModeContext";
6-
import { BrandingProvider } from "./contexts/BrandingContext";
6+
import { InitialStateProvider } from "./contexts/InitialStateContext";
77
import { AuthProvider } from "./contexts/AuthContext";
88
import ConnectionMonitor from "./components/common/ConnectionMonitor";
99
import InstallWizard from "./components/wizard/InstallWizard";
@@ -13,12 +13,12 @@ import { getQueryClient } from "./query-client";
1313
function App() {
1414
const queryClient = getQueryClient();
1515
return (
16-
<QueryClientProvider client={queryClient}>
17-
<AuthProvider>
18-
<SettingsProvider>
19-
<LinuxConfigProvider>
20-
<KubernetesConfigProvider>
21-
<BrandingProvider>
16+
<InitialStateProvider>
17+
<QueryClientProvider client={queryClient}>
18+
<AuthProvider>
19+
<SettingsProvider>
20+
<LinuxConfigProvider>
21+
<KubernetesConfigProvider>
2222
<div className="min-h-screen bg-gray-50 text-gray-900 font-sans">
2323
<BrowserRouter>
2424
<Routes>
@@ -30,18 +30,17 @@ function App() {
3030
</WizardProvider>
3131
}
3232
/>
33-
3433
<Route path="*" element={<Navigate to="/" replace />} />
3534
</Routes>
3635
</BrowserRouter>
3736
</div>
38-
</BrandingProvider>
39-
</KubernetesConfigProvider>
40-
</LinuxConfigProvider>
41-
</SettingsProvider>
42-
</AuthProvider>
43-
<ConnectionMonitor />
44-
</QueryClientProvider>
37+
</KubernetesConfigProvider>
38+
</LinuxConfigProvider>
39+
</SettingsProvider>
40+
</AuthProvider>
41+
<ConnectionMonitor />
42+
</QueryClientProvider>
43+
</InitialStateProvider>
4544
);
4645
}
4746

web/src/components/common/Logo.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import React from 'react';
22

3-
import { useBranding } from '../../contexts/BrandingContext';
3+
import { useInitialState } from '../../contexts/InitialStateContext';
44

55
export const AppIcon: React.FC<{ className?: string }> = ({ className = 'w-6 h-6' }) => {
6-
const { icon } = useBranding();
6+
const { icon } = useInitialState();
77
if (!icon) {
88
return <div className="h-6 w-6 bg-gray-200 rounded"></div>;
99
}

web/src/components/wizard/completion/KubernetesCompletionStep.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
import React, {useState} from "react";
1+
import React, { useState } from "react";
22
import Card from "../../common/Card";
33
import Button from "../../common/Button";
44
import { useKubernetesConfig } from "../../../contexts/KubernetesConfigContext";
5-
import { useBranding } from "../../../contexts/BrandingContext";
5+
import { useInitialState } from "../../../contexts/InitialStateContext";
66
import { useSettings } from "../../../contexts/SettingsContext";
77
import { CheckCircle, ClipboardCheck, Copy, Terminal } from "lucide-react";
88

99
const KubernetesCompletionStep: React.FC = () => {
1010
const [copied, setCopied] = useState(false);
1111
const { config } = useKubernetesConfig();
12-
const { title } = useBranding();
12+
const { title } = useInitialState();
1313
const { settings } = useSettings();
1414
const themeColor = settings.themeColor;
1515

web/src/components/wizard/completion/LinuxCompletionStep.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ import React from "react";
22
import Card from "../../common/Card";
33
import Button from "../../common/Button";
44
import { useLinuxConfig } from "../../../contexts/LinuxConfigContext";
5-
import { useBranding } from "../../../contexts/BrandingContext";
5+
import { useInitialState } from "../../../contexts/InitialStateContext";
66
import { useSettings } from "../../../contexts/SettingsContext";
77
import { CheckCircle, ExternalLink } from "lucide-react";
88

99
const LinuxCompletionStep: React.FC = () => {
1010
const { config } = useLinuxConfig();
11-
const { title } = useBranding();
11+
const { title } = useInitialState();
1212
const { settings } = useSettings();
1313
const themeColor = settings.themeColor;
1414

web/src/components/wizard/setup/LinuxSetupStep.tsx

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Input from "../../common/Input";
33
import Select from "../../common/Select";
44
import Button from "../../common/Button";
55
import Card from "../../common/Card";
6-
import { useBranding } from "../../../contexts/BrandingContext";
6+
import { useInitialState } from "../../../contexts/InitialStateContext";
77
import { useLinuxConfig } from "../../../contexts/LinuxConfigContext";
88
import { useWizard } from "../../../contexts/WizardModeContext";
99
import { useQuery, useMutation } from "@tanstack/react-query";
@@ -19,17 +19,17 @@ import { ChevronDown, ChevronLeft, ChevronRight } from "lucide-react";
1919
* - Error formatting: formatErrorMessage("adminConsolePort invalid") -> "Admin Console Port invalid"
2020
*/
2121
const fieldNames = {
22-
adminConsolePort: "Admin Console Port",
23-
dataDirectory: "Data Directory",
24-
localArtifactMirrorPort: "Local Artifact Mirror Port",
25-
httpProxy: "HTTP Proxy",
26-
httpsProxy: "HTTPS Proxy",
27-
noProxy: "Proxy Bypass List",
28-
networkInterface: "Network Interface",
29-
podCidr: "Pod CIDR",
30-
serviceCidr: "Service CIDR",
31-
globalCidr: "Reserved Network Range (CIDR)",
32-
cidr: "CIDR",
22+
adminConsolePort: "Admin Console Port",
23+
dataDirectory: "Data Directory",
24+
localArtifactMirrorPort: "Local Artifact Mirror Port",
25+
httpProxy: "HTTP Proxy",
26+
httpsProxy: "HTTPS Proxy",
27+
noProxy: "Proxy Bypass List",
28+
networkInterface: "Network Interface",
29+
podCidr: "Pod CIDR",
30+
serviceCidr: "Service CIDR",
31+
globalCidr: "Reserved Network Range (CIDR)",
32+
cidr: "CIDR",
3333
}
3434

3535
interface LinuxSetupStepProps {
@@ -49,7 +49,7 @@ interface ConfigError extends Error {
4949
const LinuxSetupStep: React.FC<LinuxSetupStepProps> = ({ onNext, onBack }) => {
5050
const { config, updateConfig } = useLinuxConfig();
5151
const { text } = useWizard();
52-
const { title } = useBranding();
52+
const { title } = useInitialState();
5353
const [showAdvanced, setShowAdvanced] = useState(false);
5454
const [error, setError] = useState<string | null>(null);
5555
const { token } = useAuth();
@@ -269,9 +269,9 @@ const LinuxSetupStep: React.FC<LinuxSetupStepProps> = ({ onNext, onBack }) => {
269269
options={[
270270
...(availableNetworkInterfaces.length > 0
271271
? availableNetworkInterfaces.map((iface: string) => ({
272-
value: iface,
273-
label: iface,
274-
}))
272+
value: iface,
273+
label: iface,
274+
}))
275275
: []),
276276
]}
277277
helpText={`Network interface to use for ${title}`}
@@ -296,7 +296,7 @@ const LinuxSetupStep: React.FC<LinuxSetupStepProps> = ({ onNext, onBack }) => {
296296

297297
{error && (
298298
<div className="mt-4 p-3 bg-red-50 text-red-500 rounded-md">
299-
{submitError?.errors && submitError.errors.length > 0
299+
{submitError?.errors && submitError.errors.length > 0
300300
? "Please fix the errors in the form above before proceeding."
301301
: error
302302
}
@@ -326,13 +326,13 @@ const LinuxSetupStep: React.FC<LinuxSetupStepProps> = ({ onNext, onBack }) => {
326326
* @returns The formatted error message with replaced field names
327327
*/
328328
export function formatErrorMessage(message: string) {
329-
let finalMsg = message
330-
for (const [field, fieldName] of Object.entries(fieldNames)) {
331-
// Case-insensitive regex that matches whole words only
332-
// Example: "podCidr", "PodCidr", "PODCIDR" all become "Pod CIDR"
333-
finalMsg = finalMsg.replace(new RegExp(`\\b${field}\\b`, 'gi'), fieldName)
334-
}
335-
return finalMsg
329+
let finalMsg = message
330+
for (const [field, fieldName] of Object.entries(fieldNames)) {
331+
// Case-insensitive regex that matches whole words only
332+
// Example: "podCidr", "PodCidr", "PODCIDR" all become "Pod CIDR"
333+
finalMsg = finalMsg.replace(new RegExp(`\\b${field}\\b`, 'gi'), fieldName)
334+
}
335+
return finalMsg
336336
}
337337

338338
export default LinuxSetupStep;

web/src/contexts/AuthContext.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, { createContext, useContext, useState, useEffect } from "react";
22
import { handleUnauthorized } from "../utils/auth";
3+
import { useInitialState } from "./InitialStateContext";
34

45
interface AuthContextType {
56
token: string | null;
@@ -32,17 +33,15 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
3233
}
3334
setTokenState(newToken);
3435
};
36+
// Get the installation target from initial state
37+
const { installTarget } = useInitialState()
3538

3639
// Check token validity on mount and when token changes
3740
useEffect(() => {
3841
if (token) {
39-
// Get the installation target from initial state
40-
const initialState = window.__INITIAL_STATE__ || {};
41-
const target = initialState.installTarget;
42-
4342
// Make a request to any authenticated endpoint to check token validity
4443
// Use the correct target-specific endpoint based on installation target
45-
fetch(`/api/${target}/install/installation/config`, {
44+
fetch(`/api/${installTarget}/install/installation/config`, {
4645
headers: {
4746
Authorization: `Bearer ${token}`,
4847
},

web/src/contexts/BrandingContext.tsx

Lines changed: 0 additions & 35 deletions
This file was deleted.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import React, { createContext, useContext } from "react";
2+
import { InitialState } from "../types";
3+
import { InstallationTarget, isInstallationTarget } from "../types/installation-target";
4+
5+
export const InitialStateContext = createContext<InitialState>({ title: "My App", installTarget: "linux" });
6+
7+
export const useInitialState = () => {
8+
const context = useContext(InitialStateContext);
9+
if (!context) {
10+
throw new Error("useInitialState must be used within a InitialStateProvider");
11+
}
12+
return context;
13+
};
14+
15+
function parseInstallationTarget(target: string): InstallationTarget {
16+
if (isInstallationTarget(target)) {
17+
return target;
18+
}
19+
throw new Error(`Invalid installation target: ${target}`);
20+
}
21+
22+
export const InitialStateProvider: React.FC<{ children: React.ReactNode }> = ({
23+
children,
24+
}) => {
25+
// __INITIAL_STATE__ is a global variable that can be set by the server-side rendering process
26+
// as a way to pass initial data to the client.
27+
const initialState = window.__INITIAL_STATE__ || {};
28+
29+
const state = {
30+
title: initialState.title || "My App",
31+
icon: initialState.icon,
32+
installTarget: parseInstallationTarget(initialState.installTarget || "linux"), // default to "linux" if not provided
33+
};
34+
35+
return (
36+
<InitialStateContext.Provider value={state}>
37+
{children}
38+
</InitialStateContext.Provider>
39+
);
40+
};

web/src/contexts/WizardModeContext.tsx

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import React, { createContext, useContext } from "react";
2-
import { useBranding } from "./BrandingContext";
2+
import { useInitialState } from "./InitialStateContext";
3+
import { InstallationTarget } from "../types/installation-target";
34

45
export type WizardMode = "install" | "upgrade";
5-
export type WizardTarget = "linux" | "kubernetes";
66

77
interface WizardText {
88
title: string;
@@ -59,25 +59,20 @@ const getTextVariations = (isLinux: boolean, title: string): Record<WizardMode,
5959
});
6060

6161
interface WizardModeContextType {
62-
target: WizardTarget;
62+
target: InstallationTarget;
6363
mode: WizardMode;
6464
text: WizardText;
6565
}
6666

6767
export const WizardContext = createContext<WizardModeContextType | undefined>(undefined);
6868

6969
export const WizardProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
70-
// __INITIAL_STATE__ is a global variable that can be set by the server-side rendering process
71-
// as a way to pass initial data to the client.
72-
const initialState = window.__INITIAL_STATE__ || {};
73-
const target: WizardTarget = initialState.installTarget as WizardTarget;
70+
const { title, installTarget } = useInitialState();
7471
const mode = "install"; // TODO: get mode from initial state
75-
76-
const { title } = useBranding();
77-
const isLinux = target === "linux";
72+
const isLinux = installTarget === "linux";
7873
const text = getTextVariations(isLinux, title)[mode];
7974

80-
return <WizardContext.Provider value={{ mode, target, text }}>{children}</WizardContext.Provider>;
75+
return <WizardContext.Provider value={{ mode, target: installTarget, text }}>{children}</WizardContext.Provider>;
8176
};
8277

8378
export const useWizard = (): WizardModeContextType => {

0 commit comments

Comments
 (0)