Skip to content

Commit 67cc0a6

Browse files
committed
[dashboad/self-hosted] add Setup page
1 parent da088db commit 67cc0a6

File tree

7 files changed

+234
-92
lines changed

7 files changed

+234
-92
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
})();
@@ -88,10 +96,15 @@ function App() {
8896
}
8997

9098
if (loading) {
91-
return <Loading />
99+
return (<Loading />);
100+
}
101+
if (isSetupRequired) {
102+
return (<Suspense fallback={<Loading />}>
103+
<Setup />
104+
</Suspense>);
92105
}
93106
if (!user) {
94-
return (<Login />)
107+
return (<Login />);
95108
}
96109
if (window.location.pathname.startsWith('/blocked')) {
97110
return <div className="mt-48 text-center">
@@ -117,6 +130,7 @@ function App() {
117130
<div className="container">
118131
{renderMenu(user)}
119132
<Switch>
133+
<Route path="/setup" exact component={Setup} />
120134
<Route path="/workspaces" exact component={Workspaces} />
121135
<Route path="/account" exact component={Account} />
122136
<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
@@ -25,8 +25,14 @@ async function registerApp(installationId: string, setModal: (modal: 'done' | st
2525
setModal('done');
2626
result.resolve();
2727
},
28-
onError: (error) => {
29-
setModal(error);
28+
onError: (payload) => {
29+
let errorMessage: string;
30+
if (typeof payload === "string") {
31+
errorMessage = payload;
32+
} else {
33+
errorMessage = payload.description ? payload.description : `Error: ${payload.error}`;
34+
}
35+
setModal(errorMessage);
3036
}
3137
})
3238

@@ -46,7 +52,7 @@ export default function InstallGitHubApp() {
4652
<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">
4753
<div className="flex flex-col items-center w-96 m-auto">
4854
<h3 className="text-center pb-3 text-gray-500 dark:text-gray-400">No Installation ID Found</h3>
49-
<div className="text-center pb-6 text-gray-500">Did you came here from the GitHub app's page?</div>
55+
<div className="text-center pb-6 text-gray-500">Did you come here from the GitHub app's page?</div>
5056
</div>
5157
</div>
5258
</div>

components/dashboard/src/provider-utils.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ interface OpenAuthorizeWindowParams {
4141
scopes?: string[];
4242
overrideScopes?: boolean;
4343
onSuccess?: (payload?: string) => void;
44-
onError?: (error?: string) => void;
44+
onError?: (error: string | { error: string, description?: string }) => void;
4545
}
4646

4747
async function openAuthorizeWindow(params: OpenAuthorizeWindowParams) {
@@ -75,12 +75,21 @@ async function openAuthorizeWindow(params: OpenAuthorizeWindowParams) {
7575

7676
if (typeof event.data === "string" && event.data.startsWith("success")) {
7777
killAuthWindow();
78-
onSuccess && onSuccess();
78+
onSuccess && onSuccess(event.data);
7979
}
8080
if (typeof event.data === "string" && event.data.startsWith("error:")) {
81-
const errorAsText = atob(event.data.substring("error:".length));
81+
let error: string | { error: string, description?: string } = atob(event.data.substring("error:".length));
82+
try {
83+
const payload = JSON.parse(error);
84+
if (typeof payload === "object" && payload.error) {
85+
error = { error: payload.error, description: payload.description };
86+
}
87+
} catch (error) {
88+
console.log(error);
89+
}
90+
8291
killAuthWindow();
83-
onError && onError(errorAsText);
92+
onError && onError(error);
8493
}
8594
};
8695
window.addEventListener("message", eventListener);

0 commit comments

Comments
 (0)