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

Moved logic and implemented apis for aws connect registry

jnfrati 4 лет назад
Родитель
Сommit
0096c6b31c
21 измененных файлов с 376 добавлено и 257 удалено
  1. 11 12
      dashboard/src/main/home/onboarding/Onboarding.tsx
  2. 0 93
      dashboard/src/main/home/onboarding/OnboardingState.ts
  3. 7 5
      dashboard/src/main/home/onboarding/ProvisionerForms.tsx
  4. 0 89
      dashboard/src/main/home/onboarding/StateHandler.ts
  5. 63 0
      dashboard/src/main/home/onboarding/state/StateHandler.ts
  6. 7 1
      dashboard/src/main/home/onboarding/state/StepHandler.ts
  7. 46 0
      dashboard/src/main/home/onboarding/state/index.ts
  8. 27 6
      dashboard/src/main/home/onboarding/steps/ConnectRegistry/ConnectRegistry.tsx
  9. 6 1
      dashboard/src/main/home/onboarding/steps/ConnectRegistry/ConnectRegistryState.ts
  10. 17 4
      dashboard/src/main/home/onboarding/steps/ConnectRegistry/forms/FormFlow.tsx
  11. 58 21
      dashboard/src/main/home/onboarding/steps/ConnectRegistry/forms/_AWSRegistryForm.tsx
  12. 7 5
      dashboard/src/main/home/onboarding/steps/ConnectSource.tsx
  13. 26 19
      dashboard/src/main/home/onboarding/steps/NewProject.tsx
  14. 0 0
      dashboard/src/main/home/onboarding/steps/ProvisionResources/ProvisionResources.tsx
  15. 29 0
      dashboard/src/main/home/onboarding/steps/ProvisionResources/ProvisionResourcesState.ts
  16. 0 0
      dashboard/src/main/home/onboarding/steps/ProvisionResources/forms/FormFlow.tsx
  17. 0 0
      dashboard/src/main/home/onboarding/steps/ProvisionResources/forms/_AWSProvsionerForm.tsx
  18. 0 0
      dashboard/src/main/home/onboarding/steps/ProvisionResources/forms/_DOProvisionerForm.tsx
  19. 0 0
      dashboard/src/main/home/onboarding/steps/ProvisionResources/forms/_GCPProvisionerForm.tsx
  20. 67 0
      dashboard/src/main/home/onboarding/types.ts
  21. 5 1
      dashboard/src/main/home/sidebar/ProjectSection.tsx

+ 11 - 12
dashboard/src/main/home/onboarding/Onboarding.tsx

@@ -3,31 +3,30 @@ import { useLocation } from "react-router";
 import { Context } from "shared/Context";
 import { useRouting } from "shared/routing";
 import styled from "styled-components";
-import { OnboardingState } from "./OnboardingState";
+import { useSnapshot } from "valtio";
 import Routes from "./Routes";
-import StepHandler from "./StepHandler";
+import { OFState } from "./state";
 
 const Onboarding = () => {
   const context = useContext(Context);
   const location = useLocation();
   const { pushFiltered } = useRouting();
+  const snap = useSnapshot(OFState);
 
   useEffect(() => {
-    OnboardingState.actions.initFromGlobalContext(context);
-  }, [context]);
-
-  useEffect(() => {
-    OnboardingState.actions.restoreState();
+    OFState.actions.restoreState(context.currentProject?.id);
     return () => {
-      OnboardingState.actions.clearState();
+      OFState.actions.clearState();
     };
-  }, []);
+  }, [context.currentProject?.id]);
 
   useEffect(() => {
-    if (StepHandler.currentStep.url !== location.pathname) {
-      pushFiltered(StepHandler.currentStep.url, []);
+    console.log(location);
+    if (snap.StepHandler.currentStep.url !== location.pathname) {
+      pushFiltered(snap.StepHandler.currentStep.url, []);
     }
-  }, [location.pathname]);
+  }, [location.pathname, snap.StepHandler.currentStep.url]);
+
   return (
     <StyledOnboarding>
       <Routes />

+ 0 - 93
dashboard/src/main/home/onboarding/OnboardingState.ts

@@ -1,93 +0,0 @@
-import { ContextProps } from "shared/types";
-import { proxy } from "valtio";
-import { devtools } from "valtio/utils";
-import StepHandler from "./StepHandler";
-import { State as ConnectRegistryState } from "./steps/ConnectRegistry/ConnectRegistryState";
-
-export type OnboardingStateType = {
-  [key: string]: unknown;
-  projectName: string;
-  // Null when is not setted yet.
-  isProvisionerEnabled: boolean | null;
-  userId: number | null;
-  // Check if it's the first project that will be created
-  isFirstProject: boolean | null;
-
-  actions: typeof actions;
-};
-
-const actions = {
-  setProjectName: (name: string) => {
-    OnboardingState.projectName = name;
-  },
-  setIsProvisionerEnabled: (provStatus: boolean) => {
-    OnboardingState.isProvisionerEnabled = provStatus;
-  },
-  setUserId: (userId: number) => {
-    OnboardingState.userId = userId;
-  },
-  setIsFirstProject: (isFirstProject: boolean) => {
-    OnboardingState.isFirstProject = isFirstProject;
-  },
-  initFromGlobalContext: (context: Partial<ContextProps>) => {
-    const provisionerStatus = context?.capabilities?.provisioner;
-
-    if (typeof provisionerStatus === "boolean") {
-      actions.setIsProvisionerEnabled(provisionerStatus);
-    } else {
-      actions.setIsProvisionerEnabled(null);
-    }
-
-    const userId = context?.user?.id;
-    if (typeof userId === "number") {
-      actions.setUserId(userId);
-    } else {
-      actions.setUserId(null);
-    }
-    if (context?.projects?.length >= 1) {
-      actions.setIsFirstProject(false);
-    } else {
-      actions.setIsFirstProject(true);
-    }
-  },
-  // Clear own and substates
-  clearState: () => {
-    Object.keys(OnboardingState).forEach((key) => {
-      if (key in initialState && key !== "actions") {
-        if (
-          key.toLowerCase().includes("state") &&
-          typeof OnboardingState === "object"
-        ) {
-          const subState = OnboardingState[key] as any;
-          if (
-            "clearState" in subState.actions &&
-            typeof subState?.actions?.clearState === "function"
-          ) {
-            subState.actions.clearState();
-          }
-        }
-        OnboardingState[key] = initialState[key];
-      }
-    });
-  },
-  saveState: () => {
-    const json = JSON.stringify(OnboardingState);
-    console.log(json);
-    localStorage.setItem("onboarding", json);
-  },
-  restoreState: () => {},
-};
-
-const initialState: OnboardingStateType = {
-  projectName: "",
-  isProvisionerEnabled: null,
-  userId: null,
-  isFirstProject: null,
-  selectedProvider: null,
-  actions,
-  connectRegistry_state: ConnectRegistryState,
-  stepHandler_state: StepHandler,
-};
-
-export const OnboardingState = proxy<OnboardingStateType>(initialState);
-devtools(OnboardingState, "Onboarding state");

+ 7 - 5
dashboard/src/main/home/onboarding/ProvisionerForms.tsx

@@ -1,13 +1,15 @@
 import Helper from "components/form-components/Helper";
 import TitleSection from "components/TitleSection";
-import React from "react";
+import React, { useContext } from "react";
+import { Context } from "shared/Context";
 import styled from "styled-components";
 import { useSnapshot } from "valtio";
 import ProvisionerSettings from "../provisioner/ProvisionerSettings";
-import { OnboardingState } from "./OnboardingState";
+import { OFState } from "./state";
 
 const ProvisionerForms = () => {
-  const snap = useSnapshot(OnboardingState);
+  const snap = useSnapshot(OFState);
+  const { capabilities } = useContext(Context);
   return (
     <>
       <TitleSection>Getting Started</TitleSection>
@@ -15,8 +17,8 @@ const ProvisionerForms = () => {
       <Helper>Provision a new cluster through us or link one later!</Helper>
       <ProvisionerSettings
         isInNewProject={true}
-        projectName={snap.projectName}
-        provisioner={snap.isProvisionerEnabled}
+        projectName={snap.StateHandler.project.name}
+        provisioner={capabilities.provisioner}
       />
     </>
   );

+ 0 - 89
dashboard/src/main/home/onboarding/StateHandler.ts

@@ -1,89 +0,0 @@
-import { proxy } from "valtio";
-
-type AWSRegistryConfig = {
-  provider: "aws";
-  credentials: {
-    id: string;
-  };
-  settings: {
-    registry_name: string;
-    region: string;
-  };
-};
-
-type GCPRegistryConfig = {
-  provider: "gcp";
-  credentials: {
-    id: string;
-  };
-  settings: {
-    registry_name: string;
-    gcr_url: string;
-  };
-};
-
-type AWSProvisionerConfig = {
-  skip: false;
-  provider: "aws";
-  credentials: {
-    id: string;
-  };
-  settings: {
-    cluster_name: string;
-    aws_region: string;
-    aws_machine_type: string;
-  };
-};
-
-type GCPProvisionerConfig = {
-  skip: false;
-  provider: "gcp";
-  credentials: {
-    id: string;
-  };
-  settings: {
-    gcp_region: string;
-    cluster_name: string;
-  };
-};
-
-type SkipProvisionConfig = {
-  skip: true;
-};
-
-type OnboardingState = {
-  project: {
-    id: number;
-    name: string;
-  } | null;
-  connected_source: {
-    source: "github" | "docker";
-  } | null;
-  connected_registry: GCPRegistryConfig | AWSRegistryConfig | null;
-  provision_resources:
-    | GCPProvisionerConfig
-    | AWSProvisionerConfig
-    | SkipProvisionConfig
-    | null;
-  restoreState: (state: OnboardingState) => void;
-  clearState: () => void;
-};
-
-export const State = proxy<OnboardingState>({
-  project: null,
-  connected_source: null,
-  connected_registry: null,
-  provision_resources: null,
-  restoreState: (prevState) => {
-    State.project = prevState.project;
-    State.connected_source = prevState.connected_source;
-    State.connected_registry = prevState.connected_registry;
-    State.provision_resources = prevState.provision_resources;
-  },
-  clearState: () => {
-    State.project = null;
-    State.connected_source = null;
-    State.connected_registry = null;
-    State.provision_resources = null;
-  },
-});

+ 63 - 0
dashboard/src/main/home/onboarding/state/StateHandler.ts

@@ -0,0 +1,63 @@
+import { proxy } from "valtio";
+import type {
+  AWSProvisionerConfig,
+  AWSRegistryConfig,
+  DORegistryConfig,
+  GCPProvisionerConfig,
+  GCPRegistryConfig,
+  SkipProvisionConfig,
+  SkipRegistryConnection,
+} from "../types";
+
+export type ConnectedRegistryConfig =
+  | AWSRegistryConfig
+  | GCPRegistryConfig
+  | DORegistryConfig
+  | SkipRegistryConnection;
+
+export type ProjectData = {
+  id: number;
+  name: string;
+};
+
+export type ConnectedSourceData = {
+  source: "github" | "docker";
+};
+
+export type OnboardingState = {
+  project: ProjectData | null;
+  connected_source: ConnectedSourceData | null;
+  connected_registry: ConnectedRegistryConfig | null;
+  provision_resources:
+    | GCPProvisionerConfig
+    | AWSProvisionerConfig
+    | SkipProvisionConfig
+    | null;
+  actions: {
+    restoreState: (state: OnboardingState) => void;
+    clearState: () => void;
+  };
+};
+
+export type StateKeys = keyof Omit<OnboardingState, "actions">;
+
+export const StateHandler = proxy<OnboardingState>({
+  project: null,
+  connected_source: null,
+  connected_registry: null,
+  provision_resources: null,
+  actions: {
+    restoreState: (prevState) => {
+      StateHandler.project = prevState.project;
+      StateHandler.connected_source = prevState.connected_source;
+      StateHandler.connected_registry = prevState.connected_registry;
+      StateHandler.provision_resources = prevState.provision_resources;
+    },
+    clearState: () => {
+      StateHandler.project = null;
+      StateHandler.connected_source = null;
+      StateHandler.connected_registry = null;
+      StateHandler.provision_resources = null;
+    },
+  },
+});

+ 7 - 1
dashboard/src/main/home/onboarding/StepHandler.ts → dashboard/src/main/home/onboarding/state/StepHandler.ts

@@ -1,11 +1,13 @@
 import { proxy } from "valtio";
-import { StepKey, Steps } from "./types";
+import { StepKey, Steps } from "../types";
+import { StateKeys } from "./StateHandler";
 
 type Step = {
   previous?: StepKey;
   url: string;
   next?: StepKey;
   final?: true;
+  state_key: StateKeys;
 };
 
 export type FlowType = {
@@ -21,21 +23,25 @@ const flow: FlowType = {
     new_project: {
       url: "/onboarding/new-project",
       next: "connect_source",
+      state_key: "project",
     },
     connect_source: {
       previous: "new_project",
       url: "/onboarding/source",
       next: "connect_registry",
+      state_key: "connected_source",
     },
     connect_registry: {
       previous: "connect_source",
       url: "/onboarding/registry",
       next: "provision_resources",
+      state_key: "connected_registry",
     },
     provision_resources: {
       previous: "connect_registry",
       url: "/onboarding/provision",
       final: true,
+      state_key: "provision_resources",
     },
   },
 };

+ 46 - 0
dashboard/src/main/home/onboarding/state/index.ts

@@ -0,0 +1,46 @@
+import { proxy } from "valtio";
+import { devtools } from "valtio/utils";
+import {
+  ConnectedRegistryConfig,
+  ConnectedSourceData,
+  ProjectData,
+  StateHandler,
+} from "./StateHandler";
+import { StepHandler } from "./StepHandler";
+
+export const OFState = proxy({
+  StateHandler,
+  StepHandler,
+  actions: {
+    nextStep: (data: any) => {
+      const currentStep = StepHandler.currentStep;
+      StateHandler[currentStep.state_key] = data;
+      StepHandler.actions.nextStep();
+      OFState.actions.saveState();
+    },
+    clearState: () => {
+      StateHandler.actions.clearState();
+      StepHandler.actions.clearState();
+    },
+    saveState: () => {
+      const state = JSON.stringify(OFState);
+      localStorage.setItem(`${OFState.StateHandler.project.id}`, state);
+    },
+    restoreState: (projectId: number) => {
+      const notParsedPrevState = localStorage.getItem(`${projectId}`);
+      if (!notParsedPrevState) {
+        return;
+      }
+      const prevState = JSON.parse(notParsedPrevState);
+
+      if (prevState?.StateHandler) {
+        StateHandler.actions.restoreState(prevState.StateHandler);
+      }
+      if (prevState?.StepHandler) {
+        StepHandler.actions.restoreState(prevState.StepHandler);
+      }
+    },
+  },
+});
+
+devtools(OFState, "Onboarding flow state");

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

@@ -1,11 +1,13 @@
 import Helper from "components/form-components/Helper";
 import SaveButton from "components/SaveButton";
 import TitleSection from "components/TitleSection";
-import React from "react";
+import React, { useState } from "react";
 import { useRouting } from "shared/routing";
 import styled from "styled-components";
 import { useSnapshot } from "valtio";
 import ProviderSelector from "../../components/ProviderSelector";
+import { OFState } from "../../state";
+import { SupportedProviders } from "../../types";
 
 import { State } from "./ConnectRegistryState";
 import FormFlowWrapper from "./forms/FormFlow";
@@ -13,8 +15,23 @@ import FormFlowWrapper from "./forms/FormFlow";
 const ConnectRegistry = () => {
   const snap = useSnapshot(State);
   const { pushFiltered } = useRouting();
-  const nextStep = () => {
-    pushFiltered("/onboarding/provisioner", []);
+  const [selectedProvider, setSelectedProvider] = useState<
+    SupportedProviders | ""
+  >("");
+
+  const nextStep = (skipped: boolean) => {
+    if (skipped) {
+      OFState.actions.nextStep({
+        skip: true,
+      });
+      return;
+    }
+    OFState.actions.nextStep({
+      skip: false,
+      provider: selectedProvider,
+      credentials: snap.config.credentials,
+      settings: snap.config.settings,
+    });
   };
 
   return (
@@ -27,14 +44,18 @@ const ConnectRegistry = () => {
           : "Link to an existing docker registry or continue"}
       </Helper>
       {snap.selectedProvider ? (
-        <FormFlowWrapper nextStep={nextStep} />
+        <FormFlowWrapper nextStep={() => nextStep(false)} />
       ) : (
         <>
-          <ProviderSelector selectProvider={State.actions.selectProvider} />
+          <ProviderSelector
+            selectProvider={(provider) => {
+              State.selectedProvider = provider;
+            }}
+          />
           <NextStep
             text="Continue"
             disabled={false}
-            onClick={nextStep}
+            onClick={() => nextStep(true)}
             status={""}
             makeFlush={true}
             clearPosition={true}

+ 6 - 1
dashboard/src/main/home/onboarding/steps/ConnectRegistry/ConnectRegistryState.ts

@@ -1,11 +1,15 @@
 import { proxy } from "valtio";
-import { SupportedProviders } from "../../types";
+import { ConnectedRegistryConfig } from "../../state/StateHandler";
+import { SkipRegistryConnection, SupportedProviders } from "../../types";
 
 type AllowedSteps = "credentials" | "settings" | "test_connection" | null;
 
 interface ConnectRegistryState {
   selectedProvider: SupportedProviders | null;
   currentStep: AllowedSteps;
+  config: Partial<
+    Exclude<ConnectedRegistryConfig, SkipRegistryConnection>
+  > | null;
   actions: typeof actions;
 }
 
@@ -23,6 +27,7 @@ const actions = {
 const initialState: ConnectRegistryState = {
   selectedProvider: null,
   currentStep: "credentials",
+  config: null,
   actions,
 };
 

+ 17 - 4
dashboard/src/main/home/onboarding/steps/ConnectRegistry/forms/FormFlow.tsx

@@ -1,4 +1,7 @@
-import React, { useMemo } from "react";
+import { ConnectedRegistryConfig } from "main/home/onboarding/state/StateHandler";
+import { SkipRegistryConnection } from "main/home/onboarding/types";
+import React, { useContext, useMemo } from "react";
+import { Context } from "shared/Context";
 import styled from "styled-components";
 import { useSnapshot } from "valtio";
 import { State } from "../ConnectRegistryState";
@@ -44,13 +47,22 @@ const FormTitle = {
   do: "Digital Ocean Container Registry",
 };
 
-const FormFlowWrapper: React.FC<{ nextStep: () => void }> = ({ nextStep }) => {
+type Props = {
+  nextStep: () => void;
+};
+
+const FormFlowWrapper: React.FC<Props> = ({ nextStep }) => {
   const snap = useSnapshot(State);
+  const { currentProject } = useContext(Context);
 
-  const nextFormStep = () => {
+  const nextFormStep = (
+    data: Partial<Exclude<ConnectedRegistryConfig, SkipRegistryConnection>>
+  ) => {
     if (snap.currentStep === "credentials") {
+      State.config.credentials = data.credentials;
       State.currentStep = "settings";
     } else if (snap.currentStep === "settings") {
+      State.config.settings = data.settings;
       State.currentStep = "test_connection";
     } else if (snap.currentStep === "test_connection") {
       nextStep();
@@ -68,8 +80,9 @@ const FormFlowWrapper: React.FC<{ nextStep: () => void }> = ({ nextStep }) => {
       return null;
     }
 
-    return React.createElement(currentForm, {
+    return React.createElement(currentForm as any, {
       nextFormStep,
+      project: currentProject,
     });
   }, [snap.currentStep, snap.selectedProvider]);
 

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

@@ -1,8 +1,12 @@
 import InputRow from "components/form-components/InputRow";
 import SelectRow from "components/form-components/SelectRow";
 import SaveButton from "components/SaveButton";
+import { AWSRegistryConfig } from "main/home/onboarding/types";
 import React, { useState } from "react";
 import api from "shared/api";
+import { Context } from "shared/Context";
+import { useSnapshot } from "valtio";
+import { State } from "../ConnectRegistryState";
 
 const regionOptions = [
   { value: "us-east-1", label: "US East (N. Virginia) us-east-1" },
@@ -27,12 +31,14 @@ const regionOptions = [
   { value: "sa-east-1", label: "South America (São Paulo) sa-east-1" },
 ];
 
-export const CredentialsForm: React.FC<{ nextFormStep: () => void }> = ({
-  nextFormStep,
-}) => {
+export const CredentialsForm: React.FC<{
+  nextFormStep: (data: Partial<AWSRegistryConfig>) => void;
+  project: any;
+}> = ({ nextFormStep, project }) => {
   const [accessId, setAccessId] = useState("");
   const [secretKey, setSecretKey] = useState("");
   const [buttonStatus, setButtonStatus] = useState("");
+  const [awsRegion, setAWSRegion] = useState("us-east-1");
 
   const validate = () => {
     if (!accessId) {
@@ -59,8 +65,24 @@ export const CredentialsForm: React.FC<{ nextFormStep: () => void }> = ({
       setButtonStatus(validation.error);
       return;
     }
-    // TODO: Ask alex how to request the aws_integration_id on this step
-    nextFormStep();
+
+    const res = await api.createAWSIntegration(
+      "token",
+      {
+        aws_region: awsRegion,
+        aws_access_key_id: accessId,
+        aws_secret_access_key: secretKey,
+      },
+      {
+        id: project.id,
+      }
+    );
+
+    nextFormStep({
+      credentials: {
+        id: res.data.id,
+      },
+    });
   };
 
   return (
@@ -87,6 +109,16 @@ export const CredentialsForm: React.FC<{ nextFormStep: () => void }> = ({
         width="100%"
         isRequired={true}
       />
+      <SelectRow
+        options={regionOptions}
+        width="100%"
+        value={awsRegion}
+        dropdownMaxHeight="240px"
+        setActiveValue={(x: string) => {
+          setAWSRegion(x);
+        }}
+        label="📍 AWS Region"
+      />
       <SaveButton
         text="Continue"
         disabled={false}
@@ -100,13 +132,14 @@ export const CredentialsForm: React.FC<{ nextFormStep: () => void }> = ({
   );
 };
 
-export const SettingsForm: React.FC<{ nextFormStep: () => void }> = ({
-  nextFormStep,
-}) => {
+export const SettingsForm: React.FC<{
+  nextFormStep: (data: Partial<AWSRegistryConfig>) => void;
+  project: any;
+}> = ({ nextFormStep, project }) => {
+  const snap = useSnapshot(State);
   const [registryName, setRegistryName] = useState("");
-  const [awsRegion, setAWSRegion] = useState("us-east-1");
-  const [buttonStatus, setButtonStatus] = useState("");
 
+  const [buttonStatus, setButtonStatus] = useState("");
   const validate = () => {
     if (!registryName) {
       return {
@@ -127,7 +160,20 @@ export const SettingsForm: React.FC<{ nextFormStep: () => void }> = ({
       return;
     }
 
-    nextFormStep();
+    await api.connectECRRegistry(
+      "<token>",
+      {
+        name: registryName,
+        aws_integration_id: snap.config.credentials.id,
+      },
+      { id: project.id }
+    );
+
+    nextFormStep({
+      settings: {
+        registry_name: registryName,
+      },
+    });
   };
 
   return (
@@ -142,16 +188,7 @@ export const SettingsForm: React.FC<{ nextFormStep: () => void }> = ({
         placeholder="ex: porter-awesome-registry"
         width="100%"
       />
-      <SelectRow
-        options={regionOptions}
-        width="100%"
-        value={awsRegion}
-        dropdownMaxHeight="240px"
-        setActiveValue={(x: string) => {
-          setAWSRegion(x);
-        }}
-        label="📍 AWS Region"
-      />
+
       <SaveButton
         text="Connect Registry"
         disabled={false}

+ 7 - 5
dashboard/src/main/home/onboarding/steps/ConnectSource.tsx

@@ -6,6 +6,7 @@ import { Link } from "react-router-dom";
 import api from "shared/api";
 import { useRouting } from "shared/routing";
 import styled from "styled-components";
+import { OFState } from "../state";
 
 interface GithubAppAccessData {
   username?: string;
@@ -23,7 +24,6 @@ interface GithubAppAccessData {
 const ConnectSource = () => {
   const [accountData, setAccountData] = useState<GithubAppAccessData>(null);
   const [isLoading, setIsLoading] = useState(true);
-  const { pushFiltered } = useRouting();
 
   const getAccounts = async () => {
     setIsLoading(true);
@@ -56,8 +56,10 @@ const ConnectSource = () => {
     };
   }, []);
 
-  const nextStep = () => {
-    pushFiltered("/onboarding/registry", []);
+  const nextStep = (selectedSource: "docker" | "github") => {
+    OFState.actions.nextStep({
+      source: selectedSource,
+    });
   };
 
   return (
@@ -75,7 +77,7 @@ const ConnectSource = () => {
           </ConnectToGithubButton>
           <Helper>
             No thanks, I want to deploy from a{" "}
-            <A onClick={nextStep}>Docker registry</A>
+            <A onClick={() => nextStep("docker")}>Docker registry</A>
           </Helper>
         </>
       )}
@@ -99,7 +101,7 @@ const ConnectSource = () => {
           <NextStep
             text="Continue"
             disabled={false}
-            onClick={nextStep}
+            onClick={() => nextStep("github")}
             status={""}
             makeFlush={true}
             clearPosition={true}

+ 26 - 19
dashboard/src/main/home/onboarding/steps/NewProject.tsx

@@ -1,4 +1,4 @@
-import React, { useContext, useEffect, useState } from "react";
+import React, { useContext, useEffect, useMemo, useState } from "react";
 import styled from "styled-components";
 
 import gradient from "assets/gradient.png";
@@ -8,13 +8,13 @@ import InputRow from "components/form-components/InputRow";
 import Helper from "components/form-components/Helper";
 import TitleSection from "components/TitleSection";
 import { useSnapshot } from "valtio";
-import { OnboardingState } from "../OnboardingState";
 import { useRouting } from "shared/routing";
 import { Context } from "shared/Context";
 import api from "shared/api";
 import SaveButton from "components/SaveButton";
 
 import backArrow from "assets/back_arrow.png";
+import { OFState } from "../state";
 
 type ValidationError = {
   hasError: boolean;
@@ -22,21 +22,26 @@ type ValidationError = {
 };
 
 export const NewProjectFC = () => {
-  const snap = useSnapshot(OnboardingState);
+  const snap = useSnapshot(OFState);
   const { user, setProjects, setCurrentProject } = useContext(Context);
   const { pushFiltered } = useRouting();
   const [buttonStatus, setButtonStatus] = useState("");
+  const [name, setName] = useState("");
+  const { projects } = useContext(Context);
 
-  useEffect(() => {
-    if (snap.userId !== null) {
-      window.analytics.track("provision_new-project", {
-        userId: snap.userId,
-      });
-    }
-  }, [snap.userId]);
+  // useEffect(() => {
+  //   if (snap.userId !== null) {
+  //     window.analytics.track("provision_new-project", {
+  //       userId: snap.userId,
+  //     });
+  //   }
+  // }, [snap.userId]);
+
+  const isFirstProject = useMemo(() => {
+    return !(projects?.length >= 1);
+  }, [projects]);
 
   const validateProjectName = (): ValidationError => {
-    const name = snap.projectName;
     if (name === "") {
       return {
         hasError: true,
@@ -64,7 +69,7 @@ export const NewProjectFC = () => {
   };
 
   const createProject = async () => {
-    const { projectName } = snap;
+    const projectName = name;
     setButtonStatus("loading");
     const validation = validateProjectName();
 
@@ -90,7 +95,10 @@ export const NewProjectFC = () => {
       setProjects(projectList);
       setCurrentProject(project);
 
-      pushFiltered("/onboarding/integrations", []);
+      OFState.actions.nextStep({
+        id: project.id,
+        name: project.name,
+      });
       setButtonStatus("success");
     } catch (error) {
       setButtonStatus("Couldn't create project, try again.");
@@ -100,7 +108,7 @@ export const NewProjectFC = () => {
 
   return (
     <>
-      {!snap.isFirstProject && (
+      {!isFirstProject && (
         <BackButton
           onClick={() => {
             pushFiltered("/dashboard", []);
@@ -120,19 +128,18 @@ export const NewProjectFC = () => {
       <InputWrapper>
         <ProjectIcon>
           <ProjectImage src={gradient} />
-          <Letter>
-            {snap.projectName ? snap.projectName[0].toUpperCase() : "-"}
-          </Letter>
+          <Letter>{name ? name.toUpperCase().substring(0, 1) : "-"}</Letter>
         </ProjectIcon>
         <InputRow
           type="string"
-          value={snap.projectName}
+          value={name}
           setValue={(x: string) => {
             setButtonStatus("");
-            OnboardingState.actions.setProjectName(x);
+            setName(x);
           }}
           placeholder="ex: perspective-vortex"
           width="470px"
+          disabled={buttonStatus === "loading"}
         />
       </InputWrapper>
       <NewProjectSaveButton

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


+ 29 - 0
dashboard/src/main/home/onboarding/steps/ProvisionResources/ProvisionResourcesState.ts

@@ -0,0 +1,29 @@
+import { proxy } from "valtio";
+import { SupportedProviders } from "../../types";
+
+type AllowedSteps = "credentials" | "settings" | "test_connection" | null;
+
+interface ConnectRegistryState {
+  selectedProvider: SupportedProviders | null;
+  currentStep: AllowedSteps;
+  actions: typeof actions;
+}
+
+const actions = {
+  selectProvider(provider: SupportedProviders) {
+    State.selectedProvider = provider;
+  },
+
+  clearState() {
+    State.selectedProvider = null;
+    State.currentStep = "credentials";
+  },
+};
+
+const initialState: ConnectRegistryState = {
+  selectedProvider: null,
+  currentStep: "credentials",
+  actions,
+};
+
+export const State = proxy(initialState);

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


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


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


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


+ 67 - 0
dashboard/src/main/home/onboarding/types.ts

@@ -8,3 +8,70 @@ export enum Steps {
 }
 
 export type StepKey = `${Steps}`;
+
+export type AWSRegistryConfig = {
+  skip: false;
+  provider: "aws";
+  credentials: {
+    id: string;
+  };
+  settings: {
+    registry_name: string;
+  };
+};
+
+export type GCPRegistryConfig = {
+  skip: false;
+  provider: "gcp";
+  credentials: {
+    id: string;
+  };
+  settings: {
+    registry_name: string;
+    gcr_url: string;
+  };
+};
+
+export type DORegistryConfig = {
+  skip: false;
+  provider: "do";
+  credentials: {
+    id: string;
+  };
+  settings: {
+    registry_url: string;
+  };
+};
+
+export type AWSProvisionerConfig = {
+  skip: false;
+  provider: "aws";
+  credentials: {
+    id: string;
+    arn: string;
+    region: string;
+  };
+  settings: {
+    cluster_name: string;
+    aws_region: string;
+    aws_machine_type: string;
+  };
+};
+
+export type GCPProvisionerConfig = {
+  skip: false;
+  provider: "gcp";
+  credentials: {
+    id: string;
+  };
+  settings: {
+    gcp_region: string;
+    cluster_name: string;
+  };
+};
+
+export type SkipProvisionConfig = {
+  skip: true;
+};
+
+export type SkipRegistryConnection = SkipProvisionConfig;

+ 5 - 1
dashboard/src/main/home/sidebar/ProjectSection.tsx

@@ -118,7 +118,11 @@ class ProjectSection extends Component<PropsType, StateType> {
     }
     return (
       <InitializeButton
-        onClick={() => pushFiltered(this.props, "new-project", ["project_id"])}
+        onClick={() =>
+          pushFiltered(this.props, "new-project", ["project_id"], {
+            new_project: true,
+          })
+        }
       >
         <Plus>+</Plus> Create a Project
       </InitializeButton>