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