Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/dashboard/src/start/StartPage.tsx
2500 views
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 { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
8
import Alert from "../components/Alert";
9
import { UsageLimitReachedModal } from "../components/UsageLimitReachedModal";
10
import { Heading2 } from "../components/typography/headings";
11
import { useDocumentTitle } from "../hooks/use-document-title";
12
import { gitpodHostUrl } from "../service/service";
13
import { VerifyModal } from "./VerifyModal";
14
import { useWorkspaceDefaultImageQuery } from "../data/workspaces/default-workspace-image-query";
15
import { GetWorkspaceDefaultImageResponse_Source } from "@gitpod/public-api/lib/gitpod/v1/workspace_pb";
16
import { ProductLogo } from "../components/ProductLogo";
17
import { useIsDataOps } from "../data/featureflag-query";
18
import { isGitpodIo } from "../utils";
19
import { OnaBanner } from "./OnaBanner";
20
21
export enum StartPhase {
22
Checking = 0,
23
Preparing = 1,
24
Creating = 2,
25
Starting = 3,
26
Running = 4,
27
IdeReady = 5,
28
Stopping = 6,
29
Stopped = 7,
30
}
31
32
function getPhaseTitle(phase?: StartPhase, error?: StartWorkspaceError) {
33
if (!!error) {
34
return "Oh, no! Something went wrong!";
35
}
36
switch (phase) {
37
case StartPhase.Checking:
38
return "Checking";
39
case StartPhase.Preparing:
40
return "Preparing";
41
case StartPhase.Creating:
42
return "Creating";
43
case StartPhase.Starting:
44
return "Starting";
45
case StartPhase.Running:
46
return "Starting";
47
case StartPhase.IdeReady:
48
return "Running";
49
case StartPhase.Stopping:
50
return "Stopping";
51
case StartPhase.Stopped:
52
return "Stopped";
53
default:
54
return "";
55
}
56
}
57
58
function ProgressBar(props: { phase: number; error: boolean }) {
59
const { phase, error } = props;
60
return (
61
<div className="flex mt-4 mb-6">
62
{[1, 2, 3].map((i) => {
63
let classes = "h-2 w-10 mx-1 my-2 rounded-full";
64
if (i < phase) {
65
// Already passed this phase successfully
66
classes += " bg-green-400";
67
} else if (i > phase) {
68
// Haven't reached this phase yet
69
classes += " bg-gray-200 dark:bg-gray-800";
70
} else if (error) {
71
// This phase has failed
72
classes += " bg-red-500";
73
} else {
74
// This phase is currently running
75
classes += " bg-green-400 animate-pulse";
76
}
77
return <div key={"phase-" + i} className={classes} />;
78
})}
79
</div>
80
);
81
}
82
83
export interface StartPageProps {
84
phase?: number;
85
error?: StartWorkspaceError;
86
title?: string;
87
children?: React.ReactNode;
88
showLatestIdeWarning?: boolean;
89
workspaceId?: string;
90
}
91
92
export interface StartWorkspaceError {
93
message?: string;
94
code?: number;
95
data?: any;
96
}
97
98
export function StartPage(props: StartPageProps) {
99
const { phase, error, workspaceId } = props;
100
let title = props.title || getPhaseTitle(phase, error);
101
useDocumentTitle("Starting");
102
const isDataOps = useIsDataOps();
103
104
return (
105
<div className="w-screen h-screen align-middle relative">
106
{/* OnaBanner positioned on the side when workspace is running */}
107
{isGitpodIo() && (
108
<div className="fixed bottom-4 right-4 z-1 max-w-sm">
109
<OnaBanner compact={true} />
110
</div>
111
)}
112
113
<div className="flex flex-col mx-auto items-center text-center h-screen">
114
<div className="h-1/3"></div>
115
<ProductLogo
116
className={`h-16 flex-shrink-0 ${
117
error || phase === StartPhase.Stopped || phase === StartPhase.IdeReady ? "" : "animate-bounce"
118
}`}
119
/>
120
{!isDataOps && <span className="block mt-2 text-pk-content-secondary">Gitpod Classic</span>}
121
<Heading2 className="mt-8">{title}</Heading2>
122
{typeof phase === "number" && phase < StartPhase.IdeReady && (
123
<ProgressBar phase={phase} error={!!error} />
124
)}
125
{error && error.code === ErrorCodes.NEEDS_VERIFICATION && <VerifyModal />}
126
{error && error.code === ErrorCodes.PAYMENT_SPENDING_LIMIT_REACHED && <UsageLimitReachedModal />}
127
{error && <StartError error={error} />}
128
{props.children}
129
<WarningView
130
workspaceId={workspaceId}
131
showLatestIdeWarning={props.showLatestIdeWarning}
132
error={props.error}
133
/>
134
</div>
135
</div>
136
);
137
}
138
139
function StartError(props: { error: StartWorkspaceError }) {
140
const { error } = props;
141
if (!error) {
142
return null;
143
}
144
return <p className="text-base text-gitpod-red w-96">{error.message}</p>;
145
}
146
147
function WarningView(props: { workspaceId?: string; showLatestIdeWarning?: boolean; error?: StartWorkspaceError }) {
148
const { data: imageInfo } = useWorkspaceDefaultImageQuery(props.workspaceId);
149
let useWarning: "latestIde" | "orgImage" | undefined = props.showLatestIdeWarning ? "latestIde" : undefined;
150
if (
151
props.error &&
152
props.workspaceId &&
153
imageInfo &&
154
imageInfo.source === GetWorkspaceDefaultImageResponse_Source.ORGANIZATION
155
) {
156
useWarning = "orgImage";
157
}
158
return (
159
<div>
160
{useWarning === "latestIde" && (
161
<Alert type="warning" className="mt-4 w-96">
162
This workspace is configured with the latest release (unstable) for the editor.{" "}
163
<a
164
className="gp-link"
165
target="_blank"
166
rel="noreferrer"
167
href={gitpodHostUrl.asPreferences().toString()}
168
>
169
Change Preferences
170
</a>
171
</Alert>
172
)}
173
{useWarning === "orgImage" && (
174
<Alert className="w-96 mt-4" type="warning">
175
<span className="font-medium">Could not use workspace image?</span> Try a different workspace image
176
in the yaml configuration or check the default workspace image in organization settings.
177
</Alert>
178
)}
179
</div>
180
);
181
}
182
183