Skip to content

Commit 3f0f53b

Browse files
committed
[dashboad/self-hosted] add Setup page
1 parent f4b4690 commit 3f0f53b

File tree

7 files changed

+237
-94
lines changed

7 files changed

+237
-94
lines changed

components/dashboard/src/App.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ import settingsMenu from './settings/settings-menu';
1717
import { User } from '@gitpod/gitpod-protocol';
1818
import { adminMenu } from './admin/admin-menu';
1919
import gitpodIcon from './icons/gitpod.svg';
20+
import { ErrorCodes } from '@gitpod/gitpod-protocol/lib/messaging/error';
2021

22+
const Setup = React.lazy(() => import(/* webpackPrefetch: true */ './Setup'));
2123
const Workspaces = React.lazy(() => import(/* webpackPrefetch: true */ './workspaces/Workspaces'));
2224
const Account = React.lazy(() => import(/* webpackPrefetch: true */ './settings/Account'));
2325
const Notifications = React.lazy(() => import(/* webpackPrefetch: true */ './settings/Notifications'));
@@ -43,6 +45,7 @@ function App() {
4345

4446
const [loading, setLoading] = useState<boolean>(true);
4547
const [isWhatsNewShown, setWhatsNewShown] = useState(false);
48+
const [isSetupRequired, setSetupRequired] = useState(false);
4649

4750
useEffect(() => {
4851
(async () => {
@@ -51,6 +54,11 @@ function App() {
5154
setUser(usr);
5255
} catch (error) {
5356
console.log(error);
57+
if (error && "code" in error) {
58+
if (error.code === ErrorCodes.SETUP_REQUIRED) {
59+
setSetupRequired(true);
60+
}
61+
}
5462
}
5563
setLoading(false);
5664
})();
@@ -72,10 +80,15 @@ function App() {
7280
}, [localStorage.theme]);
7381

7482
if (loading) {
75-
return <Loading />
83+
return (<Loading />);
84+
}
85+
if (isSetupRequired) {
86+
return (<Suspense fallback={<Loading />}>
87+
<Setup />
88+
</Suspense>);
7689
}
7790
if (!user) {
78-
return (<Login />)
91+
return (<Login />);
7992
}
8093
if (window.location.pathname.startsWith('/blocked')) {
8194
return <div className="mt-48 text-center">
@@ -101,6 +114,7 @@ function App() {
101114
<div className="container">
102115
{renderMenu(user)}
103116
<Switch>
117+
<Route path="/setup" exact component={Setup} />
104118
<Route path="/workspaces" exact component={Workspaces} />
105119
<Route path="/account" exact component={Account} />
106120
<Route path="/integrations" exact component={Integrations} />

components/dashboard/src/Login.tsx

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -58,21 +58,17 @@ export function Login() {
5858
login: true,
5959
host,
6060
onSuccess: () => updateUser(),
61-
onError: (error) => {
62-
if (typeof error === "string") {
63-
try {
64-
const payload = JSON.parse(error);
65-
if (typeof payload === "object" && payload.error) {
66-
if (payload.error === "email_taken") {
67-
return setErrorMessage(`Email address already exists. Log in using a different provider.`);
68-
}
69-
return setErrorMessage(payload.description ? payload.description : `Error: ${payload.error}`);
70-
}
71-
} catch (error) {
72-
console.log(error);
61+
onError: (payload) => {
62+
let errorMessage: string;
63+
if (typeof payload === "string") {
64+
errorMessage = payload;
65+
} else {
66+
errorMessage = payload.description ? payload.description : `Error: ${payload.error}`;
67+
if (payload.error === "email_taken") {
68+
errorMessage = `Email address already exists. Log in using a different provider.`;
7369
}
74-
setErrorMessage(error);
7570
}
71+
setErrorMessage(errorMessage);
7672
}
7773
});
7874
} catch (error) {

components/dashboard/src/Setup.tsx

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* Copyright (c) 2021 Gitpod GmbH. All rights reserved.
3+
* Licensed under the GNU Affero General Public License (AGPL).
4+
* See License-AGPL.txt in the project root for license information.
5+
*/
6+
7+
import { useEffect, useState } from "react";
8+
import Modal from "./components/Modal";
9+
import { getGitpodService, gitpodHostUrl } from "./service/service";
10+
import { GitIntegrationModal } from "./settings/Integrations";
11+
12+
export default function Setup() {
13+
14+
const [showModal, setShowModal] = useState<boolean>(false);
15+
16+
useEffect(() => {
17+
(async () => {
18+
const dynamicAuthProviders = await getGitpodService().server.getOwnAuthProviders();
19+
const previous = dynamicAuthProviders.filter(ap => ap.ownerId === "no-user")[0];
20+
if (previous) {
21+
await getGitpodService().server.deleteOwnAuthProvider({ id: previous.id });
22+
}
23+
})();
24+
}, []);
25+
26+
const acceptAndContinue = () => {
27+
setShowModal(true);
28+
}
29+
30+
const onAuthorize = (payload?: string) => {
31+
// run without await, so the integrated closing of new tab isn't blocked
32+
(async () => {
33+
window.location.href = gitpodHostUrl.asDashboard().toString();
34+
})();
35+
}
36+
37+
const headerText = "Configure a git integration with a GitLab or GitHub instance."
38+
39+
return <div>
40+
{!showModal && (
41+
<Modal visible={true} onClose={() => { }} closeable={false}>
42+
<h3 className="pb-2">Welcome to Gitpod 🎉</h3>
43+
<div className="border-t border-b border-gray-200 dark:border-gray-800 mt-2 -mx-6 px-6 py-4">
44+
<p className="pb-4 text-gray-500 text-base">To start using Gitpod, you will need to set up a git integration.</p>
45+
46+
<div className="flex">
47+
<span className="text-gray-500">
48+
By using Gitpod, you agree to our <a className="underline underline-thickness-thin underline-offset-small hover:text-gray-600" target="gitpod-terms" href="https://www.gitpod.io/self-hosted-terms/">terms</a>.
49+
</span>
50+
</div>
51+
</div>
52+
<div className="flex justify-end mt-6">
53+
<button className={"ml-2"} onClick={() => acceptAndContinue()}>Continue</button>
54+
</div>
55+
</Modal>
56+
)}
57+
{showModal && (
58+
<GitIntegrationModal mode="new" login={true} headerText={headerText} userId="no-user" onAuthorize={onAuthorize} />
59+
)}
60+
</div>;
61+
}

components/dashboard/src/prebuilds/InstallGitHubApp.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,14 @@ async function registerApp(installationId: string, setModal: (modal: 'done' | st
3232
setModal('done');
3333
result.resolve();
3434
},
35-
onError: (error) => {
36-
setModal(error);
35+
onError: (payload) => {
36+
let errorMessage: string;
37+
if (typeof payload === "string") {
38+
errorMessage = payload;
39+
} else {
40+
errorMessage = payload.description ? payload.description : `Error: ${payload.error}`;
41+
}
42+
setModal(errorMessage);
3743
}
3844
})
3945

@@ -53,7 +59,7 @@ export default function InstallGitHubApp() {
5359
<div className="px-6 py-3 flex justify-between space-x-2 text-gray-400 border-t border-gray-200 dark:border-gray-800 h-96">
5460
<div className="flex flex-col items-center w-96 m-auto">
5561
<h3 className="text-center pb-3 text-gray-500 dark:text-gray-400">No Installation ID Found</h3>
56-
<div className="text-center pb-6 text-gray-500">Did you came here from the GitHub app's page?</div>
62+
<div className="text-center pb-6 text-gray-500">Did you come here from the GitHub app's page?</div>
5763
</div>
5864
</div>
5965
</div>

components/dashboard/src/provider-utils.tsx

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ interface OpenAuthorizeWindowParams {
4040
host: string;
4141
scopes?: string[];
4242
onSuccess?: (payload?: string) => void;
43-
onError?: (error?: string) => void;
43+
onError?: (error: string | { error: string, description?: string }) => void;
4444
}
4545

4646
async function openAuthorizeWindow(params: OpenAuthorizeWindowParams) {
@@ -58,8 +58,9 @@ async function openAuthorizeWindow(params: OpenAuthorizeWindowParams) {
5858

5959
const newWindow = window.open(url, "gitpod-auth-window");
6060
if (!newWindow) {
61-
console.log(`Failed to open the authorize window for ${host}`);
62-
onError && onError("failed");
61+
const errorMessage = `Failed to open the authorize window for ${host}`;
62+
console.log(errorMessage);
63+
onError && onError(errorMessage);
6364
return;
6465
}
6566

@@ -77,12 +78,21 @@ async function openAuthorizeWindow(params: OpenAuthorizeWindowParams) {
7778

7879
if (typeof event.data === "string" && event.data.startsWith("success")) {
7980
killAuthWindow();
80-
onSuccess && onSuccess();
81+
onSuccess && onSuccess(event.data);
8182
}
8283
if (typeof event.data === "string" && event.data.startsWith("error:")) {
83-
const errorAsText = atob(event.data.substring("error:".length));
84+
let error: string | { error: string, description?: string } = atob(event.data.substring("error:".length));
85+
try {
86+
const payload = JSON.parse(error);
87+
if (typeof payload === "object" && payload.error) {
88+
error = { error: payload.error, description: payload.description };
89+
}
90+
} catch (error) {
91+
console.log(error);
92+
}
93+
8494
killAuthWindow();
85-
onError && onError(errorAsText);
95+
onError && onError(error);
8696
}
8797
};
8898
window.addEventListener("message", eventListener);

0 commit comments

Comments
 (0)