Skip to content

Commit 088703f

Browse files
authored
feat: Add adm install command to k8s target UI (#2417)
* Add adm install command to k8s target UI * f
1 parent 830771e commit 088703f

File tree

5 files changed

+63
-28
lines changed

5 files changed

+63
-28
lines changed

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

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,63 @@
1-
import React 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";
55
import { useBranding } from "../../../contexts/BrandingContext";
6-
import { CheckCircle, ExternalLink } from "lucide-react";
6+
import { useSettings } from "../../../contexts/SettingsContext";
7+
import { CheckCircle, ClipboardCheck, Copy, Terminal } from "lucide-react";
78

89
const KubernetesCompletionStep: React.FC = () => {
10+
const [copied, setCopied] = useState(false);
911
const { config } = useKubernetesConfig();
1012
const { title } = useBranding();
13+
const { settings } = useSettings();
14+
const themeColor = settings.themeColor;
15+
16+
const copyInstallCommand = () => {
17+
if (config.installCommand) {
18+
navigator.clipboard.writeText(config.installCommand).then(() => {
19+
setCopied(true);
20+
setTimeout(() => setCopied(false), 2000);
21+
});
22+
}
23+
};
1124

1225
return (
1326
<div className="space-y-6">
1427
<Card>
1528
<div className="flex flex-col items-center text-center py-6">
1629
<div className="flex flex-col items-center justify-center mb-6">
1730
<div className="w-16 h-16 rounded-full flex items-center justify-center mb-4">
18-
<CheckCircle className="w-10 h-10" style={{ color: "blue" }} />
31+
<CheckCircle className="w-10 h-10" style={{ color: themeColor }} />
1932
</div>
20-
<p className="text-gray-600 mt-2" data-testid="completion-message">
33+
<p className="text-gray-600 mt-2 mb-6" data-testid="completion-message">
2134
Visit the Admin Console to configure and install {title}
2235
</p>
23-
<Button
24-
className="mt-4"
25-
dataTestId="admin-console-button"
26-
onClick={() => window.open(`http://${window.location.hostname}:${config.adminConsolePort}`, "_blank")}
27-
icon={<ExternalLink className="ml-2 mr-1 h-5 w-5" />}
28-
>
29-
Visit Admin Console
30-
</Button>
36+
<div className="w-full max-w-2xl space-y-6">
37+
<div className="bg-gray-50 rounded-lg border border-gray-200 p-4">
38+
<div className="flex items-center justify-between mb-2">
39+
<h3 className="text-sm font-medium text-gray-700">Installation Command</h3>
40+
<Button
41+
variant="outline"
42+
size="sm"
43+
className="py-1 px-2 text-xs"
44+
icon={copied ? <ClipboardCheck className="w-4 h-4" /> : <Copy className="w-4 h-4" />}
45+
onClick={copyInstallCommand}
46+
>
47+
{copied ? 'Copied!' : 'Copy Command'}
48+
</Button>
49+
</div>
50+
<div className="flex items-start space-x-2 p-2 bg-white rounded border border-gray-300">
51+
<Terminal className="w-4 h-4 text-gray-400 mt-0.5 flex-shrink-0" />
52+
<code className="font-mono text-sm text-left">
53+
{config.installCommand}
54+
</code>
55+
</div>
56+
</div>
57+
<p className="text-sm text-gray-500">
58+
Run this command to access the Admin Console
59+
</p>
60+
</div>
3161
</div>
3262
</div>
3363
</Card>

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,22 @@ import Card from "../../common/Card";
33
import Button from "../../common/Button";
44
import { useLinuxConfig } from "../../../contexts/LinuxConfigContext";
55
import { useBranding } from "../../../contexts/BrandingContext";
6+
import { useSettings } from "../../../contexts/SettingsContext";
67
import { CheckCircle, ExternalLink } from "lucide-react";
78

89
const LinuxCompletionStep: React.FC = () => {
910
const { config } = useLinuxConfig();
1011
const { title } = useBranding();
12+
const { settings } = useSettings();
13+
const themeColor = settings.themeColor;
1114

1215
return (
1316
<div className="space-y-6">
1417
<Card>
1518
<div className="flex flex-col items-center text-center py-6">
1619
<div className="flex flex-col items-center justify-center mb-6">
1720
<div className="w-16 h-16 rounded-full flex items-center justify-center mb-4">
18-
<CheckCircle className="w-10 h-10" style={{ color: "blue" }} />
21+
<CheckCircle className="w-10 h-10" style={{ color: themeColor }} />
1922
</div>
2023
<p className="text-gray-600 mt-2" data-testid="completion-message">
2124
Visit the Admin Console to configure and install {title}

web/src/components/wizard/tests/KubernetesCompletionStep.test.tsx

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,43 @@ import { screen, fireEvent } from "@testing-library/react";
44
import { renderWithProviders } from "../../../test/setup.tsx";
55
import KubernetesCompletionStep from "../completion/KubernetesCompletionStep.tsx";
66

7-
// Mock window.open
8-
const mockOpen = vi.fn();
9-
Object.defineProperty(window, 'open', {
10-
value: mockOpen,
11-
writable: true,
12-
});
13-
147
describe("KubernetesCompletionStep", () => {
158
beforeEach(() => {
16-
mockOpen.mockClear();
179
// Mock window.location.hostname
1810
Object.defineProperty(window, 'location', {
1911
value: { hostname: 'localhost' },
2012
writable: true,
2113
});
2214
});
2315

24-
it("renders completion message and button", () => {
16+
it("renders completion message and copy command button", () => {
2517
renderWithProviders(<KubernetesCompletionStep />, {
2618
wrapperProps: {
2719
authenticated: true,
2820
},
2921
});
3022

3123
expect(screen.getByTestId("completion-message")).toBeInTheDocument();
32-
expect(screen.getByTestId("admin-console-button")).toBeInTheDocument();
24+
expect(screen.getByText("Copy Command")).toBeInTheDocument();
3325
});
3426

35-
it("opens admin console when button is clicked", () => {
27+
it("copies install command when button is clicked", async () => {
28+
// Mock navigator.clipboard.writeText
29+
const mockWriteText = vi.fn().mockResolvedValue(undefined);
30+
Object.defineProperty(navigator, 'clipboard', {
31+
value: { writeText: mockWriteText },
32+
writable: true,
33+
});
34+
3635
renderWithProviders(<KubernetesCompletionStep />, {
3736
wrapperProps: {
3837
authenticated: true,
3938
},
4039
});
4140

42-
const button = screen.getByTestId("admin-console-button");
41+
const button = screen.getByText("Copy Command");
4342
fireEvent.click(button);
4443

45-
expect(mockOpen).toHaveBeenCalledWith("http://localhost:8080", "_blank");
44+
expect(mockWriteText).toHaveBeenCalled();
4645
});
47-
});
46+
});

web/src/contexts/KubernetesConfigContext.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export interface KubernetesConfig {
66
httpProxy?: string;
77
httpsProxy?: string;
88
noProxy?: string;
9+
installCommand?: string;
910
}
1011

1112
interface KubernetesConfigContextType {
@@ -16,6 +17,7 @@ interface KubernetesConfigContextType {
1617

1718
const defaultKubernetesConfig: KubernetesConfig = {
1819
useProxy: false,
20+
installCommand: 'kubectl -n kotsadm port-forward svc/kotsadm 8800:3000'
1921
};
2022

2123
export const KubernetesConfigContext = createContext<KubernetesConfigContextType | undefined>(undefined);
@@ -44,4 +46,4 @@ export const useKubernetesConfig = (): KubernetesConfigContextType => {
4446
throw new Error('useKubernetesConfig must be used within a KubernetesConfigProvider');
4547
}
4648
return context;
47-
};
49+
};

web/src/test/setup.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ export const renderWithProviders = (
133133
config: {
134134
adminConsolePort: 8080,
135135
useProxy: false,
136+
installCommand: 'kubectl -n kotsadm port-forward svc/kotsadm 8800:3000',
136137
},
137138
updateConfig: vi.fn(),
138139
resetConfig: vi.fn(),

0 commit comments

Comments
 (0)