Просмотр исходного кода

Merge branch 'nico/new-onboarding-flow' of github.com:porter-dev/porter into nico/new-onboarding-flow

jnfrati 4 лет назад
Родитель
Сommit
c677f2a11e
22 измененных файлов с 434 добавлено и 194 удалено
  1. 13 11
      dashboard/src/components/Boilerplate.tsx
  2. 41 0
      dashboard/src/components/Breadcrumb.tsx
  3. 28 0
      dashboard/src/components/PageIllustration.tsx
  4. 157 0
      dashboard/src/components/ProvisionerStatus.tsx
  5. 20 20
      dashboard/src/components/Selector.tsx
  6. 1 1
      dashboard/src/main/home/Home.tsx
  7. 5 2
      dashboard/src/main/home/cluster-dashboard/env-groups/EnvGroupDashboard.tsx
  8. 1 1
      dashboard/src/main/home/modals/ClusterInstructionsModal.tsx
  9. 1 1
      dashboard/src/main/home/modals/EditInviteOrCollaboratorModal.tsx
  10. 1 1
      dashboard/src/main/home/modals/IntegrationsInstructionsModal.tsx
  11. 15 15
      dashboard/src/main/home/modals/IntegrationsModal.tsx
  12. 1 5
      dashboard/src/main/home/modals/Modal.tsx
  13. 1 1
      dashboard/src/main/home/modals/NamespaceModal.tsx
  14. 1 1
      dashboard/src/main/home/modals/UpdateClusterModal.tsx
  15. 68 54
      dashboard/src/main/home/new-project/NewProject.tsx
  16. 1 1
      dashboard/src/main/home/onboarding/Onboarding.tsx
  17. 1 0
      dashboard/src/main/home/onboarding/Routes.tsx
  18. 8 8
      dashboard/src/main/home/onboarding/steps/ConnectRegistry/ConnectRegistry.tsx
  19. 10 15
      dashboard/src/main/home/onboarding/steps/ConnectRegistry/forms/FormFlow.tsx
  20. 1 1
      dashboard/src/main/home/onboarding/steps/ConnectRegistry/forms/_AWSRegistryForm.tsx
  21. 53 56
      dashboard/src/main/home/onboarding/steps/ConnectSource.tsx
  22. 6 0
      dashboard/src/main/home/onboarding/steps/ProvisionResources/ProvisionResources.tsx

+ 13 - 11
dashboard/src/components/Boilerplate.tsx

@@ -1,16 +1,18 @@
-import React, { Component } from "react";
-import styled from "styled-components";
+import React, { useState } from "react";
 
-type PropsType = {};
+import styled from "styled-components";
 
-type StateType = {};
+type Props = {
+};
 
-export default class Boilerplate extends Component<PropsType, StateType> {
-  state = {};
+export const Boilerplate: React.FC<Props> = (props) => {
+  const [someState, setSomeState] = useState("");
 
-  render() {
-    return <StyledBoilerplate>boilerplate</StyledBoilerplate>;
-  }
-}
+  return (
+    <StyledBoilerplate>
+    </StyledBoilerplate>
+  );
+};
 
-const StyledBoilerplate = styled.div``;
+const StyledBoilerplate = styled.div`
+`;

+ 41 - 0
dashboard/src/components/Breadcrumb.tsx

@@ -0,0 +1,41 @@
+import { Steps } from "main/home/onboarding/types";
+import React, { useState } from "react";
+
+import styled from "styled-components";
+
+type Props = {
+  currentStep: string;
+  steps: { value: string, label: string }[];
+  onClickStep: (step: string) => void;
+};
+
+const Breadcrumb: React.FC<Props> = ({ currentStep, steps, onClickStep }) => {
+  return (
+    <StyledBreadcrumb>
+      {steps.map((step: { value: string, label: string }, i: number) => {
+        return (
+          <>
+          <Crumb
+            bold={currentStep === step.value}
+            onClick={() => onClickStep(step.value)}
+          >
+            {step.label}
+          </Crumb>
+          {i !== steps.length - 1 && " > "}
+          </>
+        );
+      })}
+    </StyledBreadcrumb>
+  );
+};
+
+export default Breadcrumb;
+
+const StyledBreadcrumb = styled.div`
+`;
+
+const Crumb = styled.span<{ bold: boolean }>`
+  font-weight: ${(props) => (props.bold ? "600" : "normal")};
+  font-size: 13px;
+  cursor: pointer;
+`;

Разница между файлами не показана из-за своего большого размера
+ 28 - 0
dashboard/src/components/PageIllustration.tsx


+ 157 - 0
dashboard/src/components/ProvisionerStatus.tsx

@@ -0,0 +1,157 @@
+import { Steps } from "main/home/onboarding/types";
+import React, { useState } from "react";
+
+import loading from "assets/loading.gif";
+
+import styled from "styled-components";
+
+type Props = {
+};
+
+const ProvisionerStatus: React.FC<Props> = () => {
+
+  const renderStatus = (status: string) => {
+    if (status === "successful") {
+      return (
+        <StatusIcon successful={true}>
+          <i className="material-icons">done</i>
+        </StatusIcon>
+      );
+    } else if (status === "loading") {
+      return (
+        <StatusIcon>
+          <LoadingGif src={loading} />
+        </StatusIcon>
+      );
+    } else if (status === "error") {
+      return (
+        <StatusIcon>
+          <i className="material-icons">error_outline</i>
+        </StatusIcon>
+      );
+    }
+  };
+
+  return (
+    <StyledProvisionerStatus>
+        <InfraObject>
+          <InfraHeader>
+            {renderStatus("successful")}
+            Elastic Kubernetes Service (EKS)
+          </InfraHeader>
+          <LoadingBar>
+            <LoadingFill status="successful" width="100%" />
+          </LoadingBar>
+        </InfraObject>
+        <InfraObject>
+          <InfraHeader>
+            {renderStatus("loading")}
+            Elastic Kubernetes Service (EKS)
+          </InfraHeader>
+          <LoadingBar>
+            <LoadingFill status="loading" width="90%" />
+          </LoadingBar>
+        </InfraObject>
+        <InfraObject>
+          <InfraHeader>
+            {renderStatus("error")}
+            Elastic Container Registry (ECR)
+          </InfraHeader>
+          <LoadingBar>
+            <LoadingFill status="error" width="10%" />
+          </LoadingBar>
+          <ExpandedError>
+            422 validation error: autoscaling failed because sometimes infrastructure is a bit mysterious and hard to predict.
+          </ExpandedError>
+        </InfraObject>
+    </StyledProvisionerStatus>
+  );
+};
+
+export default ProvisionerStatus;
+
+const ExpandedError = styled.div`
+  background: #ffffff22;
+  border-radius: 5px;
+  padding: 15px;
+  font-size: 13px;
+  font-family: monospace;
+  border: 1px solid #aaaabb;
+  margin-top: 17px;
+`;
+
+const LoadingFill = styled.div<{ width: string, status: string }>`
+  width: ${props => props.width};
+  background: ${props => props.status === "successful" ? "rgb(56, 168, 138)" : (
+    props.status === "error" ? "#fcba03" : "linear-gradient(to right, #8ce1ff, #616FEE)"
+  )};
+  height: 100%;
+  background-size: 250% 100%;
+  animation: moving-gradient 2s infinite;
+  animation-timing-function: ease-in-out;
+  animation-direction: alternate;
+
+  @keyframes moving-gradient {
+    0% {
+        background-position: left bottom;
+    }
+
+    100% {
+        background-position: right bottom;
+    }
+  }​
+`;
+
+const StatusIcon = styled.div<{ successful?: boolean }>`
+  display: flex;
+  align-items: center;
+  font-family: "Work Sans", sans-serif;
+  font-size: 13px;
+  color: #ffffff55;
+  max-width: 500px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+
+  > i {
+    font-size: 18px;
+    margin-right: 10px;
+    float: left;
+    color: ${props => props.successful ? "rgb(56, 168, 138)" : "#fcba03"};
+  }
+`;
+
+const LoadingGif = styled.img`
+  width: 15px;
+  height: 15px;
+  margin-right: 9px;
+  margin-bottom: 0px;
+`;
+
+const StyledProvisionerStatus = styled.div`
+  margin-top: 25px;
+`;
+
+const LoadingBar = styled.div`
+  width: 100%;
+  background: #ffffff22;
+  border: 100px;
+  margin: 15px 0 0;
+  height: 18px;
+  overflow: hidden;
+  border-radius: 100px;
+`;
+
+const InfraObject = styled.div`
+  background: #ffffff22;
+  padding: 15px 15px 17px;
+  border: 1px solid #aaaabb;
+  border-radius: 5px;
+  margin-bottom: 10px;
+`;
+
+const InfraHeader = styled.div`
+  font-size: 13px;
+  font-weight: 500;
+  display: flex;
+  align-items: center;
+`;

+ 20 - 20
dashboard/src/components/Selector.tsx

@@ -5,7 +5,7 @@ import { Context } from "shared/Context";
 type PropsType = {
   activeValue: string;
   refreshOptions?: () => void;
-  options: { value: string; label: string, icon?: any }[];
+  options: { value: string; label: string; icon?: any }[];
   addButton?: boolean;
   setActiveValue: (x: string) => void;
   width: string;
@@ -58,7 +58,7 @@ export default class Selector extends Component<PropsType, StateType> {
   renderOptionList = () => {
     let { options, activeValue } = this.props;
     return options.map(
-      (option: { value: string; label: string, icon?: any }, i: number) => {
+      (option: { value: string; label: string; icon?: any }, i: number) => {
         return (
           <Option
             key={i}
@@ -67,11 +67,11 @@ export default class Selector extends Component<PropsType, StateType> {
             onClick={() => this.handleOptionClick(option)}
             lastItem={i === options.length - 1}
           >
-            {
-              option.icon && (
-                <Icon><img src={option.icon} /></Icon>
-              )
-            }
+            {option.icon && (
+              <Icon>
+                <img src={option.icon} />
+              </Icon>
+            )}
             {option.label}
           </Option>
         );
@@ -139,14 +139,14 @@ export default class Selector extends Component<PropsType, StateType> {
     });
     return (
       <>
-        {
-          icon && (
-            <Icon><img src={icon} /></Icon>
-          )
-        }
+        {icon && (
+          <Icon>
+            <img src={icon} />
+          </Icon>
+        )}
       </>
-    )
-  }
+    );
+  };
 
   render() {
     let { activeValue } = this.props;
@@ -195,7 +195,7 @@ const Icon = styled.div`
   align-items: center;
   justify-content: center;
   overflow: visible;
-  
+
   > img {
     height: 18px;
     width: auto;
@@ -241,16 +241,16 @@ const NewOption = styled.div`
   }
 `;
 
-const Option = styled.div<{ 
-  selected: boolean; 
+const Option = styled.div<{
+  selected: boolean;
   lastItem: boolean;
   height: string;
 }>`
   width: 100%;
   border-top: 1px solid #00000000;
   border-bottom: 1px solid
-    ${props => props.lastItem ? "#ffffff00" : "#ffffff15"};
-  height: ${props => props.height || "37px"};
+    ${(props) => (props.lastItem ? "#ffffff00" : "#ffffff15")};
+  height: ${(props) => props.height || "37px"};
   font-size: 13px;
   align-items: center;
   display: flex;
@@ -261,7 +261,7 @@ const Option = styled.div<{
   white-space: nowrap;
   overflow: hidden;
   text-overflow: ellipsis;
-  background: ${props => props.selected ? "#ffffff11" : ""};
+  background: ${(props) => (props.selected ? "#ffffff11" : "")};
 
   :hover {
     background: #ffffff22;

+ 1 - 1
dashboard/src/main/home/Home.tsx

@@ -411,7 +411,7 @@ class Home extends Component<PropsType, StateType> {
             <Route
               path="/onboarding"
               render={() => {
-                return <Onboarding></Onboarding>;
+                return <Onboarding />;
               }}
             />
             <Route

+ 5 - 2
dashboard/src/main/home/cluster-dashboard/env-groups/EnvGroupDashboard.tsx

@@ -100,7 +100,7 @@ class EnvGroupDashboard extends Component<PropsType, StateType> {
             namespace={this.state.namespace}
             sortType={this.state.sortType}
             setExpandedEnvGroup={(envGroup: any) => {
-              this.setState({ expandedEnvGroup: envGroup })
+              this.setState({ expandedEnvGroup: envGroup });
             }}
           />
         </>
@@ -112,7 +112,10 @@ class EnvGroupDashboard extends Component<PropsType, StateType> {
     if (this.state.expandedEnvGroup) {
       return (
         <ExpandedEnvGroup
-          namespace={this.state.expandedEnvGroup?.metadata?.namespace || this.state.namespace}
+          namespace={
+            this.state.expandedEnvGroup?.metadata?.namespace ||
+            this.state.namespace
+          }
           currentCluster={this.props.currentCluster}
           envGroup={this.state.expandedEnvGroup}
           closeExpanded={() => this.setState({ expandedEnvGroup: null })}

+ 1 - 1
dashboard/src/main/home/modals/ClusterInstructionsModal.tsx

@@ -197,4 +197,4 @@ const Placeholder = styled.div`
 const Bold = styled.div`
   font-weight: 600;
   margin-bottom: 7px;
-`;
+`;

+ 1 - 1
dashboard/src/main/home/modals/EditInviteOrCollaboratorModal.tsx

@@ -129,4 +129,4 @@ const ModalTitle = styled.div`
   position: relative;
   white-space: nowrap;
   text-overflow: ellipsis;
-`;
+`;

+ 1 - 1
dashboard/src/main/home/modals/IntegrationsInstructionsModal.tsx

@@ -89,4 +89,4 @@ const Placeholder = styled.div`
 const Bold = styled.div`
   font-weight: 600;
   margin-bottom: 7px;
-`;
+`;

+ 15 - 15
dashboard/src/main/home/modals/IntegrationsModal.tsx

@@ -49,20 +49,20 @@ export default class IntegrationsModal extends Component<PropsType, StateType> {
 
         if (!disabled) {
           return (
-          <IntegrationOption
-            key={i}
-            disabled={disabled}
-            onClick={() => {
-              if (!disabled) {
-                setCurrentIntegration(integration.service);
-                this.context.setCurrentModal(null, null);
-              }
-            }}
-          >
-            <Icon src={icon && icon} />
-            <Label>{integrationList[integration.service].label}</Label>
-          </IntegrationOption>
-        );
+            <IntegrationOption
+              key={i}
+              disabled={disabled}
+              onClick={() => {
+                if (!disabled) {
+                  setCurrentIntegration(integration.service);
+                  this.context.setCurrentModal(null, null);
+                }
+              }}
+            >
+              <Icon src={icon && icon} />
+              <Label>{integrationList[integration.service].label}</Label>
+            </IntegrationOption>
+          );
         }
       });
     }
@@ -127,4 +127,4 @@ const Subtitle = styled.div`
   overflow: hidden;
   white-space: nowrap;
   text-overflow: ellipsis;
-`;
+`;

+ 1 - 5
dashboard/src/main/home/modals/Modal.tsx

@@ -42,11 +42,7 @@ export default class Modal extends Component<PropsType, StateType> {
           <CloseButton onClick={this.props.onRequestClose}>
             <i className="material-icons">close</i>
           </CloseButton>
-          { 
-            this.props.title && (
-              <ModalTitle>{this.props.title}</ModalTitle>
-            )
-          }
+          {this.props.title && <ModalTitle>{this.props.title}</ModalTitle>}
           {this.props.children}
         </StyledModal>
       </Overlay>

+ 1 - 1
dashboard/src/main/home/modals/NamespaceModal.tsx

@@ -147,4 +147,4 @@ const Subtitle = styled.div`
   white-space: nowrap;
   text-overflow: ellipsis;
   margin-bottom: -10px;
-`;
+`;

+ 1 - 1
dashboard/src/main/home/modals/UpdateClusterModal.tsx

@@ -223,4 +223,4 @@ const Subtitle = styled.div`
   white-space: nowrap;
   text-overflow: ellipsis;
   margin-bottom: -10px;
-`;
+`;

+ 68 - 54
dashboard/src/main/home/new-project/NewProject.tsx

@@ -8,6 +8,7 @@ import backArrow from "assets/back_arrow.png";
 import styled from "styled-components";
 
 import gradient from "assets/gradient.png";
+import PageIllustration from "components/PageIllustration";
 import { Context } from "shared/Context";
 import { isAlphanumeric } from "shared/common";
 
@@ -93,62 +94,77 @@ export const NewProjectFC = () => {
   };
 
   return (
-    <StyledNewProject>
-      {!isFirstProject && (
-        <BackButton
-          onClick={() => {
-            pushFiltered("/dashboard", []);
-          }}
-        >
-          <BackButtonImg src={backArrow} />
-        </BackButton>
-      )}
-      <FadeWrapper>
-        <TitleSection>New Project</TitleSection>
-      </FadeWrapper>
-      <FadeWrapper delay="0.7s">
-        <Helper>
-          Project name
-          <Warning highlight={validateProjectName().hasError}>
-            (lowercase letters, numbers, and "-" only)
-          </Warning>
-          <Required>*</Required>
-        </Helper>
-      </FadeWrapper>
-      <SlideWrapper delay="1.2s">
-        <InputWrapper>
-          <ProjectIcon>
-            <ProjectImage src={gradient} />
-            <Letter>{name ? name.toUpperCase().substring(0, 1) : "-"}</Letter>
-          </ProjectIcon>
-          <InputRow
-            type="string"
-            value={name}
-            setValue={(x: string) => {
-              setButtonStatus("");
-              setName(x);
-            }}
-            placeholder="ex: perspective-vortex"
-            width="470px"
-            disabled={buttonStatus === "loading"}
+    <Wrapper>
+      <StyledNewProject>
+        <PageIllustration />
+        <FadeWrapper>
+          {!isFirstProject && (
+            <BackButton
+              onClick={() => {
+                pushFiltered("/dashboard", []);
+              }}
+            >
+              <BackButtonImg src={backArrow} />
+            </BackButton>
+          )}
+          <TitleSection>New Project</TitleSection>
+        </FadeWrapper>
+        <FadeWrapper delay="0.7s">
+          <Helper>
+            Project name
+            <Warning highlight={validateProjectName().hasError}>
+              (lowercase letters, numbers, and "-" only)
+            </Warning>
+            <Required>*</Required>
+          </Helper>
+        </FadeWrapper>
+        <SlideWrapper delay="1.2s">
+          <InputWrapper>
+            <ProjectIcon>
+              <ProjectImage src={gradient} />
+              <Letter>{name ? name.toUpperCase().substring(0, 1) : "-"}</Letter>
+            </ProjectIcon>
+            <InputRow
+              type="string"
+              value={name}
+              setValue={(x: string) => {
+                setButtonStatus("");
+                setName(x);
+              }}
+              placeholder="ex: perspective-vortex"
+              width="470px"
+              disabled={buttonStatus === "loading"}
+            />
+          </InputWrapper>
+          <NewProjectSaveButton
+            text="Create Project"
+            disabled={false}
+            onClick={createProject}
+            status={buttonStatus}
+            makeFlush={true}
+            clearPosition={true}
+            statusPosition="right"
+            saveText="Creating project..."
+            successText="Project created successfully!"
           />
-        </InputWrapper>
-        <NewProjectSaveButton
-          text="Create Project"
-          disabled={false}
-          onClick={createProject}
-          status={buttonStatus}
-          makeFlush={true}
-          clearPosition={true}
-          statusPosition="right"
-          saveText="Creating project..."
-          successText="Project created successfully!"
-        />
-      </SlideWrapper>
-    </StyledNewProject>
+        </SlideWrapper>
+      </StyledNewProject>
+    </Wrapper>
   );
 };
 
+const Wrapper = styled.div`
+  max-width: 700px;
+  width: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-top: -6%;
+  padding-bottom: 5%;
+  min-width: 300px;
+  position: relative;
+`;
+
 const FadeWrapper = styled.div<{ delay?: string }>`
   opacity: 0;
   animation: fadeIn 0.5s ${(props) => props.delay || "0.2s"};
@@ -234,10 +250,8 @@ const Warning = styled.span`
 `;
 
 const StyledNewProject = styled.div`
-  width: calc(90% - 130px);
   min-width: 300px;
   position: relative;
-  margin-top: calc(50vh - 340px);
 `;
 
 const NewProjectSaveButton = styled(SaveButton)`

+ 1 - 1
dashboard/src/main/home/onboarding/Onboarding.tsx

@@ -59,7 +59,7 @@ const Onboarding = () => {
             registry_connection_id: odata.registry_connection_id,
           }
         );
-        console.log(response);
+        //console.log(response);
         if (response.data) {
           registry_connection_data = response.data;
         }

+ 1 - 0
dashboard/src/main/home/onboarding/Routes.tsx

@@ -1,5 +1,6 @@
 import React from "react";
 import { Route, Switch } from "react-router";
+import { Redirect } from "react-router-dom";
 import { OFState } from "./state";
 import ConnectRegistryWrapper from "./steps/ConnectRegistry/ConnectRegistryWrapper";
 import ConnectSource from "./steps/ConnectSource";

+ 8 - 8
dashboard/src/main/home/onboarding/steps/ConnectRegistry/ConnectRegistry.tsx

@@ -56,12 +56,12 @@ const ConnectRegistry: React.FC<{
           : "Link to an existing Docker registry or continue."}
       </Helper>
       <ProviderSelector
-          selectProvider={(provider) => {
-            if (provider !== "external") {
-              onSelectProvider(provider);
-            }
-          }}
-        />
+        selectProvider={(provider) => {
+          if (provider !== "external") {
+            onSelectProvider(provider);
+          }
+        }}
+      />
       {provider ? (
         <FormFlowWrapper
           provider={provider}
@@ -73,7 +73,7 @@ const ConnectRegistry: React.FC<{
         />
       ) : (
         <NextStep
-          text="Skip step"
+          text="Continue"
           disabled={false}
           onClick={() => onSkip()}
           status={""}
@@ -110,7 +110,7 @@ const FadeWrapper = styled.div<{ delay?: string }>`
 
 const SlideWrapper = styled.div<{ delay?: string }>`
   opacity: 0;
-  animation: slideIn 0.7s ${props => props.delay || "1.3s"};
+  animation: slideIn 0.7s ${(props) => props.delay || "1.3s"};
   animation-fill-mode: forwards;
 
   @keyframes slideIn {

+ 10 - 15
dashboard/src/main/home/onboarding/steps/ConnectRegistry/forms/FormFlow.tsx

@@ -1,4 +1,5 @@
 import { ConnectedRegistryConfig } from "main/home/onboarding/state/StateHandler";
+import Breadcrumb from "components/Breadcrumb";
 import {
   SkipRegistryConnection,
   SupportedProviders,
@@ -95,13 +96,15 @@ const FormFlowWrapper: React.FC<Props> = ({
 
   return (
     <FormWrapper>
-      <Breadcrumb>
-        <Text bold={currentStep === "credentials"}>Credentials</Text>
-        {" > "}
-        <Text bold={currentStep === "settings"}>Settings</Text>
-        {" > "}
-        <Text bold={currentStep === "test_connection"}>Test Connection</Text>
-      </Breadcrumb>
+      <Breadcrumb 
+        currentStep={currentStep}
+        steps={[
+          { value: "credentials", label: "Credentials" },
+          { value: "settings", label: "Settings" },
+          { value: "test_connection", label: "Test Connection" },
+        ]}
+        onClickStep={(step: string) => alert(step)}
+      />
       {CurrentForm}
     </FormWrapper>
   );
@@ -116,11 +119,3 @@ const FormWrapper = styled.div`
   border-bottom-left-radius: 5px;
   border-bottom-right-radius: 5px;
 `;
-
-const Text = styled.span<{ bold: boolean }>`
-  font-weight: ${(props) => (props.bold ? "600" : "normal")};
-`;
-
-const Breadcrumb = styled.div`
-  font-size: 13px;
-`;

+ 1 - 1
dashboard/src/main/home/onboarding/steps/ConnectRegistry/forms/_AWSRegistryForm.tsx

@@ -233,4 +233,4 @@ export const TestRegistryConnection: React.FC<{ nextFormStep: () => void }> = ({
 const Br = styled.div`
   width: 100%;
   height: 10px;
-`;
+`;

+ 53 - 56
dashboard/src/main/home/onboarding/steps/ConnectSource.tsx

@@ -69,61 +69,58 @@ const ConnectSource: React.FC<{
 
   return (
     <div>
-      <FadeWrapper>
-        <TitleSection>Getting Started</TitleSection>
-      </FadeWrapper>
-      <FadeWrapper delay="0.5s">
-        <Subtitle>Step 1 of 3 - Connect to GitHub</Subtitle>
-      </FadeWrapper>
-      <SlideWrapper delay="1.0s">
-        <Helper>
-          To deploy applications from your repo, you need to connect a Github
-          account.
-        </Helper>
-        {!isLoading && (!accountData || !accountData?.accounts?.length) && (
-          <>
-            <ConnectToGithubButton
-              href={`/api/integrations/github-app/install?redirect_uri=${encoded_redirect_uri}`}
-            >
-              <GitHubIcon src={github} /> Connect to GitHub
-            </ConnectToGithubButton>
-            <Helper>
-              No thanks, I want to deploy from a
-              <A onClick={() => nextStep("docker")}>Docker registry</A>.
-            </Helper>
-          </>
-        )}
-        {!isLoading && accountData?.accounts.length && (
-          <>
-            <List>
-              {accountData?.accounts.map((name, i) => {
-                return (
-                  <Row key={i} isLastItem={i === accountData.accounts.length - 1}>
-                    <i className="material-icons">bookmark</i>
-                    {name}
-                  </Row>
-                );
-              })}
-            </List>
-            <br />
-            Don't see the right repos?{" "}
-            <A href={"/api/integrations/github-app/install"}>
-              Install Porter in more repositories
-            </A>
-            <NextStep
-              text="Continue"
-              disabled={false}
-              onClick={() => nextStep("github")}
-              status={""}
-              makeFlush={true}
-              clearPosition={true}
-              statusPosition="right"
-              saveText=""
-              successText="Project created successfully!"
-            />
-          </>
-        )}
-      </SlideWrapper>
+      <TitleSection>Getting Started</TitleSection>
+      <Subtitle>Step 1 of 3 - Connect to GitHub</Subtitle>
+      <Helper>
+        To deploy applications from your repo, you need to connect a Github
+        account.
+      </Helper>
+      {!isLoading && (!accountData || !accountData?.accounts?.length) && (
+        <>
+          <ConnectToGithubButton
+            href={`/api/integrations/github-app/install?redirect_uri=${encoded_redirect_uri}`}
+          >
+            <GitHubIcon src={github} /> Connect to GitHub
+          </ConnectToGithubButton>
+          <Helper>
+            No thanks, I want to deploy from a
+            <A onClick={() => nextStep("docker")}>Docker registry</A>.
+          </Helper>
+        </>
+      )}
+      {!isLoading && accountData?.accounts.length && (
+        <>
+          <List>
+            {accountData?.accounts.map((name, i) => {
+              return (
+                <Row
+                  key={i}
+                  isLastItem={i === accountData.accounts.length - 1}
+                >
+                  <i className="material-icons">bookmark</i>
+                  {name}
+                </Row>
+              );
+            })}
+          </List>
+          <br />
+          Don't see the right repos?{" "}
+          <A href={"/api/integrations/github-app/install"}>
+            Install Porter in more repositories
+          </A>
+          <NextStep
+            text="Continue"
+            disabled={false}
+            onClick={() => nextStep("github")}
+            status={""}
+            makeFlush={true}
+            clearPosition={true}
+            statusPosition="right"
+            saveText=""
+            successText="Project created successfully!"
+          />
+        </>
+      )}
     </div>
   );
 };
@@ -147,7 +144,7 @@ const FadeWrapper = styled.div<{ delay?: string }>`
 
 const SlideWrapper = styled.div<{ delay?: string }>`
   opacity: 0;
-  animation: slideIn 0.7s ${props => props.delay || "1.3s"};
+  animation: slideIn 0.7s ${(props) => props.delay || "1.3s"};
   animation-fill-mode: forwards;
 
   @keyframes slideIn {

+ 6 - 0
dashboard/src/main/home/onboarding/steps/ProvisionResources/ProvisionResources.tsx

@@ -5,6 +5,7 @@ import React from "react";
 import { useParams } from "react-router";
 import styled from "styled-components";
 import ProviderSelector from "../../components/ProviderSelector";
+import ProvisionerStatus from "components/ProvisionerStatus";
 
 import FormFlowWrapper from "./forms/FormFlow";
 import ConnectExternalCluster from "./forms/_ConnectExternalCluster";
@@ -58,6 +59,10 @@ const ProvisionResources: React.FC<Props> = ({
         Porter automatically creates a cluster and registry in your cloud to run
         applications.
       </Helper>
+
+      <ProvisionerStatus />
+
+      {/*
       {provider ? (
         provider !== "external" ? (
           <FormFlowWrapper
@@ -80,6 +85,7 @@ const ProvisionResources: React.FC<Props> = ({
           />
         </>
       )}
+      */}
     </div>
   );
 };

Некоторые файлы не были показаны из-за большого количества измененных файлов