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

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

@@ -1,7 +1,8 @@
 import Helper from "components/form-components/Helper";
 import SaveButton from "components/SaveButton";
 import TitleSection from "components/TitleSection";
-import React, { useState } from "react";
+import React, { useEffect, useState } from "react";
+import { useLocation } from "react-router";
 import { useRouting } from "shared/routing";
 import styled from "styled-components";
 import { useSnapshot } from "valtio";
@@ -14,11 +15,19 @@ import FormFlowWrapper from "./forms/FormFlow";
 
 const ConnectRegistry = () => {
   const snap = useSnapshot(State);
-  const { pushFiltered } = useRouting();
+  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({

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

@@ -56,7 +56,7 @@ const FormFlowWrapper: React.FC<Props> = ({ nextStep }) => {
   const { currentProject } = useContext(Context);
 
   const nextFormStep = (
-    data: Partial<Exclude<ConnectedRegistryConfig, SkipRegistryConnection>>
+    data?: Partial<Exclude<ConnectedRegistryConfig, SkipRegistryConnection>>
   ) => {
     if (snap.currentStep === "credentials") {
       State.config.credentials = data.credentials;

+ 196 - 7
dashboard/src/main/home/onboarding/steps/ConnectRegistry/forms/_DORegistryForm.tsx

@@ -1,13 +1,202 @@
-import React from "react";
+import Helper from "components/form-components/Helper";
+import InputRow from "components/form-components/InputRow";
+import SelectRow from "components/form-components/SelectRow";
+import SaveButton from "components/SaveButton";
+import { DORegistryConfig } from "main/home/onboarding/types";
+import React, { useContext, useEffect, useState } from "react";
+import api from "shared/api";
+import styled from "styled-components";
+import { useSnapshot } from "valtio";
+import { State } from "../ConnectRegistryState";
 
-export const CredentialsForm = () => {
-  return <></>;
+const tierOptions = [
+  { value: "basic", label: "Basic" },
+  { value: "professional", label: "Professional" },
+];
+
+const regionOptions = [
+  { value: "ams3", label: "Amsterdam 3" },
+  { value: "blr1", label: "Bangalore 1" },
+  { value: "fra1", label: "Frankfurt 1" },
+  { value: "lon1", label: "London 1" },
+  { value: "nyc1", label: "New York 1" },
+  { value: "nyc3", label: "New York 3" },
+  { value: "sfo2", label: "San Francisco 2" },
+  { value: "sfo3", label: "San Francisco 3" },
+  { value: "sgp1", label: "Singapore 1" },
+  { value: "tor1", label: "Toronto 1" },
+];
+
+/**
+ * This will redirect to DO, and we should pass the redirection URI to be /onboarding/registry?provider=do
+ *
+ * After the oauth flow comes back, the first render will go and check if it exists a integration_id for DO in the
+ * current onboarding project, after getting it, the CredentialsForm will use nextFormStep to save the onboarding state.
+ *
+ * If it happens to be an error, it will be shown with the default error handling through the modal.
+ */
+export const CredentialsForm: React.FC<{
+  nextFormStep: (data: Partial<DORegistryConfig>) => void;
+  project: any;
+}> = ({ nextFormStep, project }) => {
+  useEffect(() => {
+    api.getOAuthIds("<token>", {}, { project_id: project?.id }).then((res) => {
+      let tgtIntegration = res.data.find((integration: any) => {
+        return integration.client === "do";
+      });
+
+      if (tgtIntegration) {
+        nextFormStep({
+          credentials: {
+            id: tgtIntegration.id,
+          },
+        });
+      }
+    });
+  }, []);
+  return (
+    <>
+      <ConnectDigitalOceanButton
+        target={"_blank"}
+        href={`/api/projects/${project?.id}/oauth/digitalocean`}
+      >
+        Connect Digital Ocean
+      </ConnectDigitalOceanButton>
+    </>
+  );
 };
 
-export const SettingsForm = () => {
-  return <></>;
+export const SettingsForm: React.FC<{
+  nextFormStep: (data: Partial<DORegistryConfig>) => void;
+  project: any;
+}> = ({ nextFormStep, project }) => {
+  const [registryUrl, setRegistryUrl] = useState("basic");
+  const [registryName, setRegistryName] = useState("");
+  const [buttonStatus] = useState("");
+  const snap = useSnapshot(State);
+
+  const submit = async () => {
+    await api.connectDORegistry(
+      "<token>",
+      {
+        name: registryName,
+        do_integration_id: snap.config.credentials.id,
+        url: registryUrl,
+      },
+      { project_id: project.id }
+    );
+    nextFormStep({
+      settings: {
+        registry_url: registryUrl,
+      },
+    });
+  };
+
+  return (
+    <>
+      <InputRow
+        type="text"
+        value={registryName}
+        setValue={(registryName: string) => setRegistryName(registryName)}
+        isRequired={true}
+        label="🏷️ Registry Name"
+        placeholder="ex: paper-straw"
+        width="100%"
+      />
+      <Helper>
+        DOC R URI, in the form{" "}
+        <CodeBlock>registry.digitalocean.com/[REGISTRY_NAME]</CodeBlock>. For
+        example, <CodeBlock>registry.digitalocean.com/porter-test</CodeBlock>.
+      </Helper>
+      <InputRow
+        type="text"
+        value={registryUrl}
+        setValue={(url: string) => setRegistryUrl(url)}
+        label="🔗 GCR URL"
+        placeholder="ex: registry.digitalocean.com/porter-test"
+        width="100%"
+        isRequired={true}
+      />
+      <SaveButton
+        text="Connect Registry"
+        disabled={false}
+        onClick={submit}
+        makeFlush={true}
+        clearPosition={true}
+        status={buttonStatus}
+        statusPosition={"right"}
+      />
+    </>
+  );
 };
 
-export const TestRegistryConnection = () => {
-  return <></>;
+export const TestRegistryConnection: React.FC<{
+  nextFormStep: () => void;
+  project: any;
+}> = ({ nextFormStep }) => {
+  return (
+    <>
+      <SaveButton
+        text="Continue"
+        disabled={false}
+        onClick={nextFormStep}
+        makeFlush={true}
+        clearPosition={true}
+        status={""}
+        statusPosition={"right"}
+      />
+    </>
+  );
 };
+
+const CodeBlock = styled.span`
+  display: inline-block;
+  background-color: #1b1d26;
+  color: white;
+  border-radius: 5px;
+  font-family: monospace;
+  padding: 2px 3px;
+  margin-top: -2px;
+  user-select: text;
+`;
+
+const ConnectDigitalOceanButton = styled.a`
+  width: 200px;
+  justify-content: center;
+  border-radius: 5px;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  font-size: 13px;
+  cursor: pointer;
+  font-family: "Work Sans", sans-serif;
+  color: white;
+  font-weight: 500;
+  padding: 10px;
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+  box-shadow: 0 5px 8px 0px #00000010;
+  cursor: ${(props: { disabled?: boolean }) =>
+    props.disabled ? "not-allowed" : "pointer"};
+
+  background: ${(props: { disabled?: boolean }) =>
+    props.disabled ? "#aaaabbee" : "#616FEEcc"};
+  :hover {
+    background: ${(props: { disabled?: boolean }) =>
+      props.disabled ? "" : "#505edddd"};
+  }
+
+  > i {
+    color: white;
+    width: 18px;
+    height: 18px;
+    font-weight: 600;
+    font-size: 12px;
+    border-radius: 20px;
+    display: flex;
+    align-items: center;
+    margin-right: 5px;
+    justify-content: center;
+  }
+`;

+ 12 - 0
dashboard/src/shared/api.tsx

@@ -33,6 +33,17 @@ const connectGCRRegistry = baseApi<
   return `/api/projects/${pathParams.id}/registries`;
 });
 
+const connectDORegistry = baseApi<
+  {
+    name: string;
+    do_integration_id: string;
+    url: string;
+  },
+  { project_id: number }
+>("POST", (pathParams) => {
+  return `/api/projects/${pathParams.project_id}/registries`;
+});
+
 const createAWSIntegration = baseApi<
   {
     aws_region: string;
@@ -1049,6 +1060,7 @@ export default {
   checkAuth,
   connectECRRegistry,
   connectGCRRegistry,
+  connectDORegistry,
   createAWSIntegration,
   overwriteAWSIntegration,
   createDOCR,