jnfrati 4 лет назад
Родитель
Сommit
b6b4d9a27f

+ 25 - 19
dashboard/src/main/home/onboarding/Onboarding.tsx

@@ -4,34 +4,40 @@ import { Context } from "shared/Context";
 import { useRouting } from "shared/routing";
 import { useRouting } from "shared/routing";
 import styled from "styled-components";
 import styled from "styled-components";
 import { useSnapshot } from "valtio";
 import { useSnapshot } from "valtio";
+import { devtools } from "valtio/utils";
 import Routes from "./Routes";
 import Routes from "./Routes";
 import { OFState } from "./state";
 import { OFState } from "./state";
+import { useSteps } from "./state/StepHandler";
 
 
 const Onboarding = () => {
 const Onboarding = () => {
-  const context = useContext(Context);
-  const location = useLocation();
-  const { pushFiltered } = useRouting();
-  const snap = useSnapshot(OFState);
+  useSteps();
 
 
   useEffect(() => {
   useEffect(() => {
-    OFState.actions.initializeState(context.currentProject?.id);
+    let sub = devtools(OFState, "Onboarding flow state");
     return () => {
     return () => {
-      OFState.actions.clearState();
+      sub();
     };
     };
-  }, [context.currentProject?.id]);
+  }, []);
 
 
-  useEffect(() => {
-    if (snap.StepHandler.finishedOnboarding) {
-      OFState.actions.clearState();
-      pushFiltered("/dashboard", []);
-    } else if (snap.StepHandler?.currentStep?.url !== location.pathname) {
-      pushFiltered(snap.StepHandler.currentStep.url, []);
-    }
-  }, [
-    location.pathname,
-    snap.StepHandler?.currentStep?.url,
-    snap.StepHandler?.finishedOnboarding,
-  ]);
+  // useEffect(() => {
+  //   OFState.actions.initializeState(context.currentProject?.id);
+  //   return () => {
+  //     OFState.actions.clearState();
+  //   };
+  // }, [context.currentProject?.id]);
+
+  // useEffect(() => {
+  //   if (snap.StepHandler.finishedOnboarding) {
+  //     OFState.actions.clearState();
+  //     pushFiltered("/dashboard", []);
+  //   } else if (snap.StepHandler?.currentStep?.url !== location.pathname) {
+  //     pushFiltered(snap.StepHandler.currentStep.url, []);
+  //   }
+  // }, [
+  //   location.pathname,
+  //   snap.StepHandler?.currentStep?.url,
+  //   snap.StepHandler?.finishedOnboarding,
+  // ]);
 
 
   return (
   return (
     <StyledOnboarding>
     <StyledOnboarding>

+ 28 - 6
dashboard/src/main/home/onboarding/Routes.tsx

@@ -1,25 +1,47 @@
 import React from "react";
 import React from "react";
 import { Route, Switch } from "react-router";
 import { Route, Switch } from "react-router";
+import { useSnapshot } from "valtio";
+import { OFState } from "./state";
 import ConnectRegistry from "./steps/ConnectRegistry/ConnectRegistry";
 import ConnectRegistry from "./steps/ConnectRegistry/ConnectRegistry";
+import ConnectRegistryWrapper from "./steps/ConnectRegistry/ConnectRegistryWrapper";
 import ConnectSource from "./steps/ConnectSource";
 import ConnectSource from "./steps/ConnectSource";
 import { NewProjectFC } from "./steps/NewProject";
 import { NewProjectFC } from "./steps/NewProject";
 import ProvisionResources from "./steps/ProvisionResources/ProvisionResources";
 import ProvisionResources from "./steps/ProvisionResources/ProvisionResources";
 
 
+const handleContinue = (data?: any) => {
+  OFState.actions.nextStep("continue", data);
+};
+
+const handleSkip = () => {
+  OFState.actions.nextStep("skip");
+};
+
 export const Routes = () => {
 export const Routes = () => {
+  const snap = useSnapshot(OFState);
   return (
   return (
     <>
     <>
       <Switch>
       <Switch>
         <Route path={`/onboarding/new-project`}>
         <Route path={`/onboarding/new-project`}>
-          <NewProjectFC />
+          <NewProjectFC
+            onSuccess={(data) => OFState.actions.nextStep("continue", data)}
+          />
         </Route>
         </Route>
         <Route path={`/onboarding/source`}>
         <Route path={`/onboarding/source`}>
-          <ConnectSource />
+          <ConnectSource
+            onSuccess={(data) => OFState.actions.nextStep("continue", data)}
+          />
         </Route>
         </Route>
-        <Route path={`/onboarding/registry`}>
-          <ConnectRegistry />
+        <Route path={["/onboarding/registry/:step?"]}>
+          <ConnectRegistryWrapper />
         </Route>
         </Route>
-        <Route path={`/onboarding/provision`}>
-          <ProvisionResources />
+        <Route path={[`/onboarding/provision/:step?`]}>
+          <ProvisionResources
+            onSelectProvider={handleContinue}
+            onSaveSettings={handleContinue}
+            onSaveCredentials={handleContinue}
+            onSkip={handleSkip}
+            project={snap.StateHandler.project}
+          />
         </Route>
         </Route>
       </Switch>
       </Switch>
     </>
     </>

+ 59 - 4
dashboard/src/main/home/onboarding/state/StateHandler.ts

@@ -7,6 +7,7 @@ import type {
   GCPRegistryConfig,
   GCPRegistryConfig,
   SkipProvisionConfig,
   SkipProvisionConfig,
   SkipRegistryConnection,
   SkipRegistryConnection,
+  SupportedProviders,
 } from "../types";
 } from "../types";
 
 
 export type ConnectedRegistryConfig =
 export type ConnectedRegistryConfig =
@@ -33,23 +34,24 @@ export type ConnectedSourceData = {
 export type OnboardingState = {
 export type OnboardingState = {
   project: ProjectData | null;
   project: ProjectData | null;
   connected_source: ConnectedSourceData | null;
   connected_source: ConnectedSourceData | null;
-  connected_registry: ConnectedRegistryConfig | null;
-  provision_resources: ProvisionerConfig | null;
+  connected_registry: any | null;
+  provision_resources: Partial<ProvisionerConfig> | null;
   actions: {
   actions: {
     restoreState: (state: OnboardingState) => void;
     restoreState: (state: OnboardingState) => void;
     clearState: () => void;
     clearState: () => void;
+    [key: string]: any;
   };
   };
 };
 };
 
 
 export type StateKeys = keyof Omit<OnboardingState, "actions">;
 export type StateKeys = keyof Omit<OnboardingState, "actions">;
 
 
-export const StateHandler = proxy<OnboardingState>({
+export const StateHandler = proxy({
   project: null,
   project: null,
   connected_source: null,
   connected_source: null,
   connected_registry: null,
   connected_registry: null,
   provision_resources: null,
   provision_resources: null,
   actions: {
   actions: {
-    restoreState: (prevState) => {
+    restoreState: (prevState: any) => {
       StateHandler.project = prevState.project;
       StateHandler.project = prevState.project;
       StateHandler.connected_source = prevState.connected_source;
       StateHandler.connected_source = prevState.connected_source;
       StateHandler.connected_registry = prevState.connected_registry;
       StateHandler.connected_registry = prevState.connected_registry;
@@ -61,5 +63,58 @@ export const StateHandler = proxy<OnboardingState>({
       StateHandler.connected_registry = null;
       StateHandler.connected_registry = null;
       StateHandler.provision_resources = null;
       StateHandler.provision_resources = null;
     },
     },
+    saveProjectData: (projectData: any) => {
+      StateHandler.project = projectData;
+    },
+    saveSelectedSource: (source: string) => {
+      StateHandler.connected_source = source;
+    },
+    skipRegistryConnection: () => {
+      StateHandler.connected_registry = {
+        skip: true,
+      };
+    },
+    saveRegistryProvider: (provider: string) => {
+      StateHandler.connected_registry = {
+        skip: false,
+        provider: provider as any,
+      };
+    },
+    saveRegistryCredentials: (credentials: any) => {
+      StateHandler.connected_registry = {
+        ...StateHandler.connected_registry,
+        credentials,
+      };
+    },
+    saveRegistrySettings: (settings: any) => {
+      StateHandler.connected_registry = {
+        ...StateHandler.connected_registry,
+        settings,
+      };
+    },
+
+    skipResourceProvisioning: () => {
+      StateHandler.provision_resources = {
+        skip: true,
+      };
+    },
+    saveResourceProvisioningProvider: (provider: string) => {
+      StateHandler.provision_resources = {
+        skip: provider === "external",
+        provider: provider as any,
+      };
+    },
+    saveResourceProvisioningCredentials: (credentials: any) => {
+      StateHandler.provision_resources = {
+        ...StateHandler.connected_registry,
+        credentials,
+      };
+    },
+    saveResourceProvisioningSettings: (settings: any) => {
+      StateHandler.provision_resources = {
+        ...StateHandler.connected_registry,
+        settings,
+      };
+    },
   },
   },
 });
 });

+ 176 - 23
dashboard/src/main/home/onboarding/state/StepHandler.ts

@@ -1,13 +1,36 @@
-import { proxy } from "valtio";
+import { useEffect } from "react";
+import { useLocation } from "react-router";
+import { useRouting } from "shared/routing";
+import { proxy, useSnapshot } from "valtio";
+import { devtools } from "valtio/utils";
 import { StepKey, Steps } from "../types";
 import { StepKey, Steps } from "../types";
 import { StateKeys } from "./StateHandler";
 import { StateKeys } from "./StateHandler";
 
 
 type Step = {
 type Step = {
   previous?: StepKey;
   previous?: StepKey;
   url: string;
   url: string;
-  next?: StepKey;
   final?: true;
   final?: true;
-  state_key: StateKeys;
+  substeps?: {
+    [key in string]: SubStep;
+  };
+  on?: ActionHandler;
+  execute?: {
+    on: {
+      skip?: string;
+      continue?: string;
+    };
+  };
+};
+
+type SubStep = Omit<Step, "previous"> & {
+  parent: StepKey;
+  previous?: string;
+};
+
+export type Action = "skip" | "continue";
+type ActionHandler = {
+  skip?: string;
+  continue: string;
 };
 };
 
 
 export type FlowType = {
 export type FlowType = {
@@ -22,39 +45,138 @@ const flow: FlowType = {
   steps: {
   steps: {
     new_project: {
     new_project: {
       url: "/onboarding/new-project",
       url: "/onboarding/new-project",
-      next: "connect_source",
-      state_key: "project",
+      on: {
+        continue: "connect_source",
+      },
+      execute: {
+        on: {
+          continue: "saveProjectData",
+        },
+      },
     },
     },
     connect_source: {
     connect_source: {
       previous: "new_project",
       previous: "new_project",
       url: "/onboarding/source",
       url: "/onboarding/source",
-      next: "connect_registry",
-      state_key: "connected_source",
+      on: {
+        continue: "connect_registry",
+      },
+      execute: {
+        on: {
+          continue: "saveSelectedSource",
+        },
+      },
     },
     },
     connect_registry: {
     connect_registry: {
       previous: "connect_source",
       previous: "connect_source",
       url: "/onboarding/registry",
       url: "/onboarding/registry",
-      next: "provision_resources",
-      state_key: "connected_registry",
+      on: {
+        skip: "provision_resources",
+        continue: "connect_registry.credentials",
+      },
+      execute: {
+        on: {
+          skip: "skipRegistryConnection",
+          continue: "saveRegistryProvider",
+        },
+      },
+      substeps: {
+        credentials: {
+          url: "/onboarding/registry/credentials",
+          on: {
+            continue: "connect_registry.settings",
+          },
+          parent: "connect_registry",
+          execute: {
+            on: {
+              continue: "saveRegistryCredentials",
+            },
+          },
+        },
+        settings: {
+          previous: "credentials",
+          url: "/onboarding/registry/settings",
+          on: {
+            continue: "connect_registry.test_connection",
+          },
+          parent: "connect_registry",
+          execute: {
+            on: {
+              continue: "saveRegistrySettings",
+            },
+          },
+        },
+        test_connection: {
+          previous: "settings",
+          url: "/onboarding/registry/test_connection",
+          on: {
+            continue: "provision_resources",
+          },
+          parent: "connect_registry",
+        },
+      },
     },
     },
     provision_resources: {
     provision_resources: {
-      final: true,
       previous: "connect_registry",
       previous: "connect_registry",
       url: "/onboarding/provision",
       url: "/onboarding/provision",
-      state_key: "provision_resources",
+      on: {
+        skip: "provision_resources.connect_own_cluster",
+        continue: "provision_resources.credentials",
+      },
+      execute: {
+        on: {
+          skip: "skipResourceProvisioning",
+          continue: "saveResourceProvisioningProvider",
+        },
+      },
+      substeps: {
+        connect_own_cluster: {
+          url: "/onboarding/provision/connect_own_cluster",
+          on: {
+            continue: "clean_up",
+          },
+          parent: "provision_resources",
+        },
+        credentials: {
+          url: "/onboarding/provision/credentials",
+          on: { continue: "settings" },
+          parent: "provision_resources",
+          execute: {
+            on: {
+              continue: "saveResourceProvisioningCredentials",
+            },
+          },
+        },
+        settings: {
+          previous: "credentials",
+          url: "/onboarding/provision/settings",
+          on: {
+            continue: "clean_up",
+          },
+          parent: "provision_resources",
+          execute: {
+            on: {
+              continue: "saveResourceProvisioningSettings",
+            },
+          },
+        },
+      },
+    },
+    clean_up: {
+      final: true,
+      url: "/dashboard?tab=provisioner",
     },
     },
   },
   },
 };
 };
 
 
 type StepHandlerType = {
 type StepHandlerType = {
   flow: FlowType;
   flow: FlowType;
-  currentStepName: StepKey;
-  currentStep: Step;
-  finishedOnboarding: boolean;
+  currentStepName: string;
+  currentStep: Step | SubStep;
   actions: {
   actions: {
-    nextStep: () => void;
+    nextStep: (action?: Action) => void;
     clearState: () => void;
     clearState: () => void;
     restoreState: (prevState: StepHandlerType) => void;
     restoreState: (prevState: StepHandlerType) => void;
+    getStep: (nextStepName: string) => Step | SubStep;
   };
   };
 };
 };
 
 
@@ -62,28 +184,59 @@ export const StepHandler: StepHandlerType = proxy({
   flow,
   flow,
   currentStepName: flow.initial,
   currentStepName: flow.initial,
   currentStep: flow.steps[flow.initial],
   currentStep: flow.steps[flow.initial],
-  finishedOnboarding: false,
   actions: {
   actions: {
-    nextStep: () => {
+    nextStep: (action: Action = "continue") => {
       const cs = StepHandler.currentStep;
       const cs = StepHandler.currentStep;
+
       if (cs.final) {
       if (cs.final) {
-        StepHandler.finishedOnboarding = true;
         return;
         return;
       }
       }
-      const nextStepName = cs.next;
-      const nextStep = flow.steps[nextStepName];
-      StepHandler.currentStep = nextStep;
+
+      const nextStepName = cs.on[action];
+
+      if (!nextStepName) {
+        throw new Error(
+          "No next step name found, fix the action triggering nextStep"
+        );
+      }
+
       StepHandler.currentStepName = nextStepName;
       StepHandler.currentStepName = nextStepName;
+      StepHandler.currentStep = StepHandler.actions.getStep(nextStepName);
       return;
       return;
     },
     },
+    getStep: (nextStepName: string) => {
+      const [stepName, substep] = nextStepName.split(".");
+
+      const step = flow.steps[stepName as Steps];
+
+      let nextStep: Step | SubStep = step;
+
+      if (substep) {
+        nextStep = step.substeps[substep];
+      }
+      return nextStep;
+    },
     clearState: () => {
     clearState: () => {
       StepHandler.currentStepName = flow.initial;
       StepHandler.currentStepName = flow.initial;
       StepHandler.currentStep = flow.steps[flow.initial];
       StepHandler.currentStep = flow.steps[flow.initial];
-      StepHandler.finishedOnboarding = false;
     },
     },
     restoreState: (prevState) => {
     restoreState: (prevState) => {
       StepHandler.currentStepName = prevState.currentStepName;
       StepHandler.currentStepName = prevState.currentStepName;
-      StepHandler.currentStep = prevState.currentStep;
+      StepHandler.currentStep = StepHandler.actions.getStep(
+        prevState.currentStepName
+      );
     },
     },
   },
   },
 });
 });
+
+export const useSteps = () => {
+  const snap = useSnapshot(StepHandler);
+  const location = useLocation();
+  const { pushFiltered } = useRouting();
+  useEffect(() => {
+    if (snap.currentStepName === "clean_up") {
+      StepHandler.actions.clearState();
+    }
+    pushFiltered(snap.currentStep.url, ["tab"]);
+  }, [location.pathname, snap.currentStep?.url]);
+};

+ 12 - 8
dashboard/src/main/home/onboarding/state/index.ts

@@ -1,7 +1,7 @@
 import { proxy, subscribe } from "valtio";
 import { proxy, subscribe } from "valtio";
 import { devtools, subscribeKey } from "valtio/utils";
 import { devtools, subscribeKey } from "valtio/utils";
 import { StateHandler } from "./StateHandler";
 import { StateHandler } from "./StateHandler";
-import { StepHandler } from "./StepHandler";
+import { Action, StepHandler } from "./StepHandler";
 import { State as ConnectRegistryState } from "../steps/ConnectRegistry/ConnectRegistryState";
 import { State as ConnectRegistryState } from "../steps/ConnectRegistry/ConnectRegistryState";
 import { State as ProvisionResourcesState } from "../steps/ProvisionResources/ProvisionResourcesState";
 import { State as ProvisionResourcesState } from "../steps/ProvisionResources/ProvisionResourcesState";
 
 
@@ -14,10 +14,16 @@ export const OFState = proxy({
       OFState.actions.restoreState(projectId);
       OFState.actions.restoreState(projectId);
       OFState.subscriptions = OFState.actions.subscribeToSubstates();
       OFState.subscriptions = OFState.actions.subscribeToSubstates();
     },
     },
-    nextStep: (data: any) => {
-      const currentStep = StepHandler.currentStep;
-      StateHandler[currentStep.state_key] = data;
-      StepHandler.actions.nextStep();
+    nextStep: (action?: Action, data?: any) => {
+      const functionToExecute = StepHandler?.currentStep?.execute?.on[action];
+      if (functionToExecute) {
+        const actions: any = StateHandler.actions;
+        const executable = actions[functionToExecute];
+        if (typeof executable === "function") {
+          executable(data);
+        }
+      }
+      StepHandler.actions.nextStep(action);
       OFState.actions.saveState();
       OFState.actions.saveState();
     },
     },
     clearState: () => {
     clearState: () => {
@@ -29,7 +35,7 @@ export const OFState = proxy({
     saveState: () => {
     saveState: () => {
       const state = JSON.stringify(OFState);
       const state = JSON.stringify(OFState);
       localStorage.setItem(
       localStorage.setItem(
-        `onboarding-${OFState.StateHandler.project.id}`,
+        `onboarding-${OFState.StateHandler.project?.id}`,
         state
         state
       );
       );
     },
     },
@@ -84,5 +90,3 @@ export const OFState = proxy({
     provision_resources: ProvisionResourcesState,
     provision_resources: ProvisionResourcesState,
   },
   },
 });
 });
-
-devtools(OFState, "Onboarding flow state");

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

@@ -1,72 +1,68 @@
 import Helper from "components/form-components/Helper";
 import Helper from "components/form-components/Helper";
 import SaveButton from "components/SaveButton";
 import SaveButton from "components/SaveButton";
 import TitleSection from "components/TitleSection";
 import TitleSection from "components/TitleSection";
-import React, { useEffect, useState } from "react";
-import { useLocation } from "react-router";
-import { useRouting } from "shared/routing";
+import React, { useState } from "react";
+import { useParams } from "react-router";
+
 import styled from "styled-components";
 import styled from "styled-components";
-import { useSnapshot } from "valtio";
 import ProviderSelector from "../../components/ProviderSelector";
 import ProviderSelector from "../../components/ProviderSelector";
-import { OFState } from "../../state";
 import { SupportedProviders } from "../../types";
 import { SupportedProviders } from "../../types";
 
 
-import { State } from "./ConnectRegistryState";
 import FormFlowWrapper from "./forms/FormFlow";
 import FormFlowWrapper from "./forms/FormFlow";
 
 
-const ConnectRegistry = () => {
-  const snap = useSnapshot(State);
-  const { getQueryParam } = useRouting();
-  const location = useLocation();
-  const [selectedProvider, setSelectedProvider] = useState<
-    SupportedProviders | ""
-  >("");
-
-  useEffect(() => {
-    const provider = getQueryParam("provider");
-    if (provider === "aws" || provider === "gcp" || provider === "do") {
-      State.selectedProvider = provider;
-    }
-  }, [location]);
-
-  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,
-    });
+const ConnectRegistry: React.FC<{
+  provider: SupportedProviders;
+  project: {
+    id: number;
+    name: string;
   };
   };
+  onSelectProvider: (provider: SupportedProviders) => void;
+  onSaveCredentials: (credentials: any) => void;
+  onSaveSettings: (settings: any) => void;
+  onSuccess: () => void;
+  onSkip: () => void;
+}> = ({
+  onSelectProvider,
+  onSaveCredentials,
+  onSaveSettings,
+  onSuccess,
+  onSkip,
+  project,
+  provider,
+}) => {
+  const { step } = useParams<any>();
 
 
   return (
   return (
     <>
     <>
       <TitleSection>Getting Started</TitleSection>
       <TitleSection>Getting Started</TitleSection>
       <Subtitle>Step 2 of 3</Subtitle>
       <Subtitle>Step 2 of 3</Subtitle>
       <Helper>
       <Helper>
-        {snap.selectedProvider
+        {provider
           ? "Link to an existing Docker registry. Don't worry if you don't know what this is"
           ? "Link to an existing Docker registry. Don't worry if you don't know what this is"
           : "Link to an existing docker registry or continue"}
           : "Link to an existing docker registry or continue"}
       </Helper>
       </Helper>
-      {snap.selectedProvider ? (
-        <FormFlowWrapper nextStep={() => nextStep(false)} />
+      {provider ? (
+        <FormFlowWrapper
+          provider={provider}
+          onSaveCredentials={onSaveCredentials}
+          onSaveSettings={onSaveSettings}
+          onSuccess={onSuccess}
+          project={project}
+          currentStep={step}
+        />
       ) : (
       ) : (
         <>
         <>
           <ProviderSelector
           <ProviderSelector
             selectProvider={(provider) => {
             selectProvider={(provider) => {
               if (provider !== "external") {
               if (provider !== "external") {
-                State.selectedProvider = provider;
+                onSelectProvider(provider);
               }
               }
             }}
             }}
           />
           />
           <NextStep
           <NextStep
             text="Skip step"
             text="Skip step"
             disabled={false}
             disabled={false}
-            onClick={() => nextStep(true)}
+            onClick={() => onSkip()}
             status={""}
             status={""}
             makeFlush={true}
             makeFlush={true}
             clearPosition={true}
             clearPosition={true}

+ 23 - 0
dashboard/src/main/home/onboarding/steps/ConnectRegistry/ConnectRegistryWrapper.tsx

@@ -0,0 +1,23 @@
+import React from "react";
+import { useSnapshot } from "valtio";
+import { OFState } from "../../state";
+import ConnectRegistry from "./ConnectRegistry";
+
+const ConnectRegistryWrapper = () => {
+  const snap = useSnapshot(OFState);
+  return (
+    <ConnectRegistry
+      provider={snap.StateHandler.connected_registry.provider}
+      project={snap.StateHandler.project}
+      onSelectProvider={(provider) =>
+        OFState.actions.nextStep("continue", provider)
+      }
+      onSaveCredentials={(data) => OFState.actions.nextStep("continue", data)}
+      onSaveSettings={(data) => OFState.actions.nextStep("continue", data)}
+      onSuccess={() => OFState.actions.nextStep("continue")}
+      onSkip={() => OFState.actions.nextStep("skip")}
+    />
+  );
+};
+
+export default ConnectRegistryWrapper;

+ 33 - 28
dashboard/src/main/home/onboarding/steps/ConnectRegistry/forms/FormFlow.tsx

@@ -1,10 +1,10 @@
 import { ConnectedRegistryConfig } from "main/home/onboarding/state/StateHandler";
 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 {
+  SkipRegistryConnection,
+  SupportedProviders,
+} from "main/home/onboarding/types";
+import React, { useMemo } from "react";
 import styled from "styled-components";
 import styled from "styled-components";
-import { useSnapshot } from "valtio";
-import { State } from "../ConnectRegistryState";
 import {
 import {
   CredentialsForm as AWSCredentialsForm,
   CredentialsForm as AWSCredentialsForm,
   SettingsForm as AWSSettingsForm,
   SettingsForm as AWSSettingsForm,
@@ -48,55 +48,60 @@ const FormTitle = {
 };
 };
 
 
 type Props = {
 type Props = {
-  nextStep: () => void;
+  provider: SupportedProviders;
+  onSaveCredentials: (credentials: any) => void;
+  onSaveSettings: (settings: any) => void;
+  onSuccess: () => void;
+  project: { id: number; name: string };
+  currentStep: "credentials" | "settings" | "test_connection";
 };
 };
 
 
-const FormFlowWrapper: React.FC<Props> = ({ nextStep }) => {
-  const snap = useSnapshot(State);
-  const { currentProject } = useContext(Context);
-
+const FormFlowWrapper: React.FC<Props> = ({
+  onSaveCredentials,
+  onSaveSettings,
+  onSuccess,
+  provider,
+  project,
+  currentStep,
+}) => {
   const nextFormStep = (
   const nextFormStep = (
     data?: Partial<Exclude<ConnectedRegistryConfig, SkipRegistryConnection>>
     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();
+    if (currentStep === "credentials") {
+      onSaveCredentials(data.credentials);
+    } else if (currentStep === "settings") {
+      onSaveSettings(data.settings);
+    } else if (currentStep === "test_connection") {
+      onSuccess();
     }
     }
   };
   };
 
 
   const CurrentForm = useMemo(() => {
   const CurrentForm = useMemo(() => {
-    const providerSteps = Forms[snap.selectedProvider];
+    const providerSteps = Forms[provider];
     if (!providerSteps) {
     if (!providerSteps) {
       return null;
       return null;
     }
     }
 
 
-    const currentForm = providerSteps[snap.currentStep];
+    const currentForm = providerSteps[currentStep];
     if (!currentForm) {
     if (!currentForm) {
       return null;
       return null;
     }
     }
 
 
     return React.createElement(currentForm as any, {
     return React.createElement(currentForm as any, {
       nextFormStep,
       nextFormStep,
-      project: currentProject,
+      project,
     });
     });
-  }, [snap.currentStep, snap.selectedProvider]);
+  }, [provider, currentStep]);
 
 
   return (
   return (
     <>
     <>
-      {FormTitle[snap.selectedProvider]}
+      {FormTitle[provider]}
       <Breadcrumb>
       <Breadcrumb>
-        <Text bold={snap.currentStep === "credentials"}>Credentials</Text>
+        <Text bold={currentStep === "credentials"}>Credentials</Text>
         {" > "}
         {" > "}
-        <Text bold={snap.currentStep === "settings"}>Settings</Text>
+        <Text bold={currentStep === "settings"}>Settings</Text>
         {" > "}
         {" > "}
-        <Text bold={snap.currentStep === "test_connection"}>
-          Test Connection
-        </Text>
+        <Text bold={currentStep === "test_connection"}>Test Connection</Text>
       </Breadcrumb>
       </Breadcrumb>
       {CurrentForm}
       {CurrentForm}
     </>
     </>

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

@@ -21,7 +21,9 @@ interface GithubAppAccessData {
  *
  *
  * The other option would be skip integration, that will skip the whole github connection flow.
  * The other option would be skip integration, that will skip the whole github connection flow.
  */
  */
-const ConnectSource = () => {
+const ConnectSource: React.FC<{
+  onSuccess: (data: any) => void;
+}> = ({ onSuccess }) => {
   const [accountData, setAccountData] = useState<GithubAppAccessData>(null);
   const [accountData, setAccountData] = useState<GithubAppAccessData>(null);
   const [isLoading, setIsLoading] = useState(true);
   const [isLoading, setIsLoading] = useState(true);
 
 
@@ -57,9 +59,7 @@ const ConnectSource = () => {
   }, []);
   }, []);
 
 
   const nextStep = (selectedSource: "docker" | "github") => {
   const nextStep = (selectedSource: "docker" | "github") => {
-    OFState.actions.nextStep({
-      source: selectedSource,
-    });
+    onSuccess(selectedSource);
   };
   };
 
 
   return (
   return (

+ 5 - 14
dashboard/src/main/home/onboarding/steps/NewProject.tsx

@@ -1,4 +1,4 @@
-import React, { useContext, useEffect, useMemo, useState } from "react";
+import React, { useContext, useMemo, useState } from "react";
 import styled from "styled-components";
 import styled from "styled-components";
 
 
 import gradient from "assets/gradient.png";
 import gradient from "assets/gradient.png";
@@ -7,36 +7,27 @@ import { isAlphanumeric } from "shared/common";
 import InputRow from "components/form-components/InputRow";
 import InputRow from "components/form-components/InputRow";
 import Helper from "components/form-components/Helper";
 import Helper from "components/form-components/Helper";
 import TitleSection from "components/TitleSection";
 import TitleSection from "components/TitleSection";
-import { useSnapshot } from "valtio";
 import { useRouting } from "shared/routing";
 import { useRouting } from "shared/routing";
 import { Context } from "shared/Context";
 import { Context } from "shared/Context";
 import api from "shared/api";
 import api from "shared/api";
 import SaveButton from "components/SaveButton";
 import SaveButton from "components/SaveButton";
 
 
 import backArrow from "assets/back_arrow.png";
 import backArrow from "assets/back_arrow.png";
-import { OFState } from "../state";
 
 
 type ValidationError = {
 type ValidationError = {
   hasError: boolean;
   hasError: boolean;
   description?: string;
   description?: string;
 };
 };
 
 
-export const NewProjectFC = () => {
-  const snap = useSnapshot(OFState);
+export const NewProjectFC: React.FC<{
+  onSuccess: (projectData: { id: number; name: string }) => void;
+}> = ({ onSuccess }) => {
   const { user, setProjects, setCurrentProject } = useContext(Context);
   const { user, setProjects, setCurrentProject } = useContext(Context);
   const { pushFiltered } = useRouting();
   const { pushFiltered } = useRouting();
   const [buttonStatus, setButtonStatus] = useState("");
   const [buttonStatus, setButtonStatus] = useState("");
   const [name, setName] = useState("");
   const [name, setName] = useState("");
   const { projects } = useContext(Context);
   const { projects } = useContext(Context);
 
 
-  // useEffect(() => {
-  //   if (snap.userId !== null) {
-  //     window.analytics.track("provision_new-project", {
-  //       userId: snap.userId,
-  //     });
-  //   }
-  // }, [snap.userId]);
-
   const isFirstProject = useMemo(() => {
   const isFirstProject = useMemo(() => {
     return !(projects?.length >= 1);
     return !(projects?.length >= 1);
   }, [projects]);
   }, [projects]);
@@ -95,7 +86,7 @@ export const NewProjectFC = () => {
       setProjects(projectList);
       setProjects(projectList);
       setCurrentProject(project);
       setCurrentProject(project);
 
 
-      OFState.actions.nextStep({
+      onSuccess({
         id: project.id,
         id: project.id,
         name: project.name,
         name: project.name,
       });
       });

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

@@ -12,39 +12,46 @@ import { OFState } from "../../state";
 import { State } from "./ProvisionResourcesState";
 import { State } from "./ProvisionResourcesState";
 import FormFlowWrapper from "./forms/FormFlow";
 import FormFlowWrapper from "./forms/FormFlow";
 import ConnectExternalCluster from "./forms/_ConnectExternalCluster";
 import ConnectExternalCluster from "./forms/_ConnectExternalCluster";
+import { SupportedProviders } from "../../types";
 
 
-const ProvisionResources = () => {
-  const snap = useSnapshot(State);
-  const globalFormSnap = useSnapshot(OFState);
-  const { getQueryParam } = useRouting();
-  const location = useLocation();
+type Props = {
+  provider: SupportedProviders | "external";
+  project: {
+    id: number;
+    name: string;
+  };
+  shouldProvisionRegistry: boolean;
+  onSelectProvider: (provider: SupportedProviders | "external") => void;
+  onSaveCredentials: (credentials: any) => void;
+  onSaveSettings: (settings: any) => void;
+  onSuccess: () => void;
+  onSkip: () => void;
+};
 
 
-  useEffect(() => {
-    const provider = getQueryParam("provider");
-    if (provider === "aws" || provider === "gcp" || provider === "do") {
-      State.selectedProvider = provider;
-    }
-  }, [location]);
+const ProvisionResources: React.FC<Props> = ({
+  provider,
+  project,
+  shouldProvisionRegistry,
+  onSelectProvider,
+  onSaveCredentials,
+  onSaveSettings,
+  onSuccess,
+}) => {
+  // const globalFormSnap = useSnapshot(OFState);
+  // const { getQueryParam } = useRouting();
+  // const location = useLocation();
 
 
-  useEffect(() => {
-    const connectedRegistry = globalFormSnap.StateHandler.connected_registry;
-    State.shouldProvisionRegistry = !!connectedRegistry?.skip;
-  }, [globalFormSnap.StateHandler.connected_registry]);
+  // useEffect(() => {
+  //   const provider = getQueryParam("provider");
+  //   if (provider === "aws" || provider === "gcp" || provider === "do") {
+  //     State.selectedProvider = provider;
+  //   }
+  // }, [location]);
 
 
-  const nextStep = (skipped: boolean) => {
-    if (skipped) {
-      OFState.actions.nextStep({
-        skip: true,
-      });
-      return;
-    }
-    OFState.actions.nextStep({
-      skip: false,
-      provider: snap.selectedProvider,
-      credentials: snap.config.credentials,
-      settings: snap.config.settings,
-    });
-  };
+  // useEffect(() => {
+  //   const connectedRegistry = globalFormSnap.StateHandler.connected_registry;
+  //   State.shouldProvisionRegistry = !!connectedRegistry?.skip;
+  // }, [globalFormSnap.StateHandler.connected_registry]);
 
 
   return (
   return (
     <>
     <>
@@ -54,22 +61,19 @@ const ProvisionResources = () => {
         Porter automatically creates a cluster and registry in your cloud to run
         Porter automatically creates a cluster and registry in your cloud to run
         applications.
         applications.
       </Helper>
       </Helper>
-      {snap.selectedProvider ? (
-        snap.selectedProvider !== "external" ? (
+      {provider ? (
+        provider !== "external" ? (
           <FormFlowWrapper nextStep={() => nextStep(false)} />
           <FormFlowWrapper nextStep={() => nextStep(false)} />
         ) : (
         ) : (
-          <ConnectExternalCluster
-            nextStep={() => nextStep(true)}
-            project={globalFormSnap.StateHandler.project}
-          />
+          <ConnectExternalCluster nextStep={onSuccess} project={project} />
         )
         )
       ) : (
       ) : (
         <>
         <>
           <ProviderSelector
           <ProviderSelector
             selectProvider={(provider) => {
             selectProvider={(provider) => {
-              State.selectedProvider = provider;
+              onSelectProvider(provider);
             }}
             }}
-            enableExternal={true}
+            enableExternal={!shouldProvisionRegistry}
           />
           />
         </>
         </>
       )}
       )}

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


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

@@ -5,6 +5,7 @@ export enum Steps {
   CONNECT_SOURCE = "connect_source",
   CONNECT_SOURCE = "connect_source",
   CONNECT_REGISTRY = "connect_registry",
   CONNECT_REGISTRY = "connect_registry",
   PROVISION_RESOURCES = "provision_resources",
   PROVISION_RESOURCES = "provision_resources",
+  CLEAN_UP = "clean_up",
 }
 }
 
 
 export type StepKey = `${Steps}`;
 export type StepKey = `${Steps}`;
@@ -87,3 +88,29 @@ export type SkipProvisionConfig = {
 };
 };
 
 
 export type SkipRegistryConnection = SkipProvisionConfig;
 export type SkipRegistryConnection = SkipProvisionConfig;
+
+interface Onboarding {
+  current_step: string;
+
+  project_id: number;
+  project_name: string;
+
+  connected_source: "docker" | "github";
+
+  skip_registry_connection: boolean;
+
+  registry_connection_credentials_id: number;
+  registry_connection_settings_url: string;
+  registry_connection_settings_name: string;
+
+  skip_resource_provision: boolean;
+
+  resource_provision_credentials_id: number;
+  resource_provision_credentials_arn: string;
+  resource_provision_credentials_region: string;
+
+  resource_provision_settings_cluster_name: string;
+  resource_provision_settings_region: string;
+  resource_provision_settings_tier: string;
+  resource_provision_settings_machine_type: string;
+}