Justin Rhee 3 anos atrás
pai
commit
b005a81706

BIN
dashboard/src/assets/add-circle.png


BIN
dashboard/src/assets/creds.png


+ 199 - 43
dashboard/src/components/CredentialsForm.tsx

@@ -3,6 +3,9 @@ import styled from "styled-components";
 
 
 import api from "shared/api";
 import api from "shared/api";
 import aws from "assets/aws.png";
 import aws from "assets/aws.png";
+import credsIcon from "assets/creds.png";
+import addCircle from "assets/add-circle.png";
+
 import { Context } from "shared/Context";
 import { Context } from "shared/Context";
 
 
 import Heading from "components/form-components/Heading";
 import Heading from "components/form-components/Heading";
@@ -12,6 +15,7 @@ import SaveButton from "./SaveButton";
 
 
 type Props = {
 type Props = {
   goBack: () => void;
   goBack: () => void;
+  proceed: (x: any) => void;
 };
 };
 
 
 type AWSCredential = {
 type AWSCredential = {
@@ -25,12 +29,16 @@ type AWSCredential = {
 
 
 const CredentialsForm: React.FC<Props> = ({
 const CredentialsForm: React.FC<Props> = ({
   goBack,
   goBack,
+  proceed,
 }) => {
 }) => {
   const { currentProject } = useContext(Context);
   const { currentProject } = useContext(Context);
   const [awsCredentials, setAWSCredentials] = useState<AWSCredential[]>(null);
   const [awsCredentials, setAWSCredentials] = useState<AWSCredential[]>(null);
   const [isLoading, setIsLoading] = useState(true);
   const [isLoading, setIsLoading] = useState(true);
   const [awsAccessKeyID, setAWSAccessKeyID] = useState("");
   const [awsAccessKeyID, setAWSAccessKeyID] = useState("");
   const [awsSecretAccessKey, setAWSSecretAccessKey] = useState("");
   const [awsSecretAccessKey, setAWSSecretAccessKey] = useState("");
+  const [selectedCredentials, setSelectedCredentials] = useState<AWSCredential>(null);
+  const [showCreateForm, setShowCreateForm] = useState(false);
+  const [createStatus, setCreateStatus] = useState("");
 
 
   useEffect(() => {
   useEffect(() => {
     api
     api
@@ -53,44 +61,110 @@ const CredentialsForm: React.FC<Props> = ({
       });
       });
   }, [currentProject]);
   }, [currentProject]);
 
 
+  const createCreds = () => {
+    setCreateStatus("loading");
+
+    api
+      .createAWSIntegration(
+        "<token>",
+        {
+          // Hardcoded for backward-compatibility
+          // TODO: remove
+          aws_region: "us-east-f",
+
+          aws_access_key_id: awsAccessKeyID,
+          aws_secret_access_key: awsSecretAccessKey,
+          aws_assume_role_arn: "",
+        },
+        {
+          id: currentProject.id,
+        }
+      )
+      .then(({ data }) => {
+        setCreateStatus("successful");
+        proceed(data.id);
+      })
+      .catch((err) => {
+        console.error(err);
+        setCreateStatus("Error creating credentials");
+      });
+  };
+
   const renderContent = () => {
   const renderContent = () => {
-    if (awsCredentials.length > 0) {
+    if (awsCredentials.length > 0 && !showCreateForm) {
       return (
       return (
-        <CredentialList>
-          {
-            awsCredentials.map((cred: AWSCredential, i: number) => {
-              return (
-                <Credential key={cred.id} isLast={awsCredentials.length - 1 === i}>
-                  <Name>{cred.aws_arn || "n/a"}</Name>
-                </Credential>
-              );
-            })
-          }
-        </CredentialList>
+        <>
+          <CredentialList>
+            {
+              awsCredentials.map((cred: AWSCredential, i: number) => {
+                return (
+                  <Credential 
+                    key={cred.id}
+                    isSelected={cred.id === selectedCredentials?.id}
+                    onClick={() => {
+                      if (cred.id === selectedCredentials?.id) {
+                        setSelectedCredentials(null);
+                      } else {
+                        setSelectedCredentials(cred);
+                      }
+                    }}
+                  >
+                    <Icon src={credsIcon} />
+                    <Name>{cred.aws_arn || "n/a"}</Name>
+                  </Credential>
+                );
+              })
+            }
+            <CreateRow onClick={() => {
+              setShowCreateForm(true);
+              setSelectedCredentials(null);
+            }}>
+              <Icon src={addCircle} />
+              Add new AWS credentials
+            </CreateRow>
+          </CredentialList>
+          <Br height="34px" />
+          <SaveButton
+            disabled={!selectedCredentials && true}
+            onClick={() => proceed(selectedCredentials.id)}
+            clearPosition
+            text="Continue"
+          />
+        </>
       );
       );
     }
     }
     return (
     return (
       <>
       <>
-        <InputRow 
-          type="string"
-          value={awsAccessKeyID}
-          setValue={(e: string) => setAWSAccessKeyID(e)}
-          label="👤 AWS access ID"
-          placeholder="ex: AKIAIOSFODNN7EXAMPLE"
-          isRequired
-        />
-        <InputRow 
-          type="password"
-          value={awsSecretAccessKey}
-          setValue={(e: string) => setAWSSecretAccessKey(e)}
-          label="🔒 AWS secret key"
-          placeholder="○ ○ ○ ○ ○ ○ ○ ○ ○"
-          isRequired
-        />
-        <Br />
+        <StyledForm>
+          {
+            awsCredentials.length > 0 && (
+              <CloseButton width="172px" onClick={() => setShowCreateForm(false)}>
+                <i className="material-icons">close</i>
+              </CloseButton>
+            )
+          }
+          <InputRow 
+            type="string"
+            value={awsAccessKeyID}
+            setValue={(e: string) => setAWSAccessKeyID(e)}
+            label="👤 AWS access ID"
+            placeholder="ex: AKIAIOSFODNN7EXAMPLE"
+            isRequired
+          />
+          <InputRow 
+            type="password"
+            value={awsSecretAccessKey}
+            setValue={(e: string) => setAWSSecretAccessKey(e)}
+            label="🔒 AWS secret key"
+            placeholder="○ ○ ○ ○ ○ ○ ○ ○ ○"
+            isRequired
+          />
+        </StyledForm>
         <SaveButton
         <SaveButton
           disabled={awsAccessKeyID === "" || awsSecretAccessKey === ""}
           disabled={awsAccessKeyID === "" || awsSecretAccessKey === ""}
-          onClick={() => console.log("go straight to editor form")}
+          onClick={createCreds}
+          status={createStatus}
+          statusPosition="right"
           clearPosition
           clearPosition
           text="Continue"
           text="Continue"
         />
         />
@@ -99,16 +173,18 @@ const CredentialsForm: React.FC<Props> = ({
   }
   }
 
 
   return (
   return (
-    <StyledCredentialsForm>
+    <>
       <Heading isAtTop>
       <Heading isAtTop>
-        <BackButton onClick={goBack}>
-          <i className="material-icons">keyboard_backspace</i>
+        <BackButton width="140px" onClick={goBack}>
+          <i className="material-icons">first_page</i>
+          Select cloud
         </BackButton>
         </BackButton>
+        <Spacer />
         <Img src={aws} />
         <Img src={aws} />
-        AWS credentials
+        Set AWS credentials
       </Heading>
       </Heading>
       <Helper>
       <Helper>
-        Select your credentials from the list below, or link a new set of credentials:
+        Select your credentials from the list below, or add a new set of credentials:
       </Helper>
       </Helper>
       {
       {
         isLoading ? (
         isLoading ? (
@@ -117,15 +193,61 @@ const CredentialsForm: React.FC<Props> = ({
           renderContent()
           renderContent()
         )
         )
       }
       }
-    </StyledCredentialsForm>
+    </>
   );
   );
 };
 };
 
 
 export default CredentialsForm;
 export default CredentialsForm;
 
 
-const Br = styled.div`
+const CloseButton = styled.div`
+  position: absolute;
+  top: 15px;
+  right: 15px;
+  padding: 5px;
+  border-radius: 100px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+  background: #ffffff11;
+  :hover {
+    background: #ffffff22;
+    > i {
+      color: #ffffff;
+    }
+  }
+  > i {
+    font-size: 20px;
+    color: #aaaabb;
+  }
+`;
+
+const Spacer = styled.div`
+  height: 1px;
+  width: 17px;
+`;
+
+const Icon = styled.img`
+  width: 15px;
+  margin-right: 15px;
+`;
+
+const CreateRow = styled.div`
+  height: 50px;
+  display: flex;
+  cursor: pointer;
+  align-items: center;
+  font-size: 13px;
+  padding: 20px;
+  background: #ffffff11;
+  :hover {
+    background: #ffffff18; 
+  }
+`;
+
+const Br = styled.div<{ height?: string }>`
   width: 100%;
   width: 100%;
-  height: 25px;
+  height: ${props => props.height || "20px"};
 `;
 `;
 
 
 const Img = styled.img`
 const Img = styled.img`
@@ -134,6 +256,33 @@ const Img = styled.img`
 `;
 `;
 
 
 const BackButton = styled.div`
 const BackButton = styled.div`
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  cursor: pointer;
+  font-size: 13px;
+  height: 35px;
+  padding: 5px 13px;
+  padding-right: 15px;
+  border: 1px solid #ffffff55;
+  border-radius: 100px;
+  width: ${(props: { width: string }) => props.width};
+  color: white;
+  background: #ffffff11;
+
+  :hover {
+    background: #ffffff22;
+  }
+
+  > i {
+    color: white;
+    font-size: 16px;
+    margin-right: 6px;
+    margin-left: -2px;
+  }
+`;
+
+const BackArrow = styled.div`
   width: 30px;
   width: 30px;
   height: 30px;
   height: 30px;
   margin-left: -5px;
   margin-left: -5px;
@@ -157,17 +306,22 @@ const BackButton = styled.div`
 `;
 `;
 
 
 const Name = styled.div`
 const Name = styled.div`
-  font-size: 14px;
+  font-size: 13px;
   font-weight: 500;
   font-weight: 500;
 `;
 `;
 
 
-const Credential = styled.div<{ isLast?: boolean}>`
+const Credential = styled.div<{ isLast?: boolean; isSelected?: boolean }>`
   height: 50px;
   height: 50px;
   display: flex;
   display: flex;
+  cursor: pointer;
   align-items: center;
   align-items: center;
   padding: 20px;
   padding: 20px;
   border-bottom: ${props => props.isLast ? "" : "1px solid #7a7b80"};
   border-bottom: ${props => props.isLast ? "" : "1px solid #7a7b80"};
-  background: #ffffff11;
+  background: ${props => props.isSelected ? "#ffffff33" : "#ffffff11"};
+
+  :hover {
+    background: ${props => props.isSelected ? "" : "#ffffff18"}; 
+  }
 `;
 `;
 
 
 const CredentialList = styled.div`
 const CredentialList = styled.div`
@@ -176,10 +330,12 @@ const CredentialList = styled.div`
   border-radius: 5px;
   border-radius: 5px;
 `;
 `;
 
 
-const StyledCredentialsForm = styled.div`
-  padding: 30px;
+const StyledForm = styled.div`
+  position: relative;
+  padding: 15px 30px 25px;
   border-radius: 5px;
   border-radius: 5px;
   background: #26292e;
   background: #26292e;
   border: 1px solid #494b4f;
   border: 1px solid #494b4f;
   font-size: 13px;
   font-size: 13px;
+  margin-bottom: 30px;
 `;
 `;

+ 13 - 0
dashboard/src/components/ProvisionerFlow.tsx

@@ -4,6 +4,7 @@ import styled from "styled-components";
 import { integrationList } from "shared/common";
 import { integrationList } from "shared/common";
 import { Context } from "shared/Context";
 import { Context } from "shared/Context";
 
 
+import ProvisionerForm from "components/ProvisionerForm";
 import CredentialsForm from "components/CredentialsForm";
 import CredentialsForm from "components/CredentialsForm";
 import Helper from "components/form-components/Helper";
 import Helper from "components/form-components/Helper";
 
 
@@ -16,6 +17,7 @@ const ProvisionerFlow: React.FC<Props> = ({
 }) => {
 }) => {
   const { usage, hasBillingEnabled } = useContext(Context);
   const { usage, hasBillingEnabled } = useContext(Context);
   const [currentStep, setCurrentStep] = useState("cloud");
   const [currentStep, setCurrentStep] = useState("cloud");
+  const [credentialId, setCredentialId] = useState(null);
 
 
   const isUsageExceeded = useMemo(() => {
   const isUsageExceeded = useMemo(() => {
     if (!hasBillingEnabled) {
     if (!hasBillingEnabled) {
@@ -56,6 +58,17 @@ const ProvisionerFlow: React.FC<Props> = ({
     return (
     return (
       <CredentialsForm 
       <CredentialsForm 
         goBack={() => setCurrentStep("cloud")}
         goBack={() => setCurrentStep("cloud")}
+        proceed={(id) => {
+          setCredentialId(id);
+          setCurrentStep("cluster");
+        }}
+      />
+    );
+  } else if (currentStep === "cluster") {
+    return (
+      <ProvisionerForm
+        goBack={() => setCurrentStep("credentials")}
+        credentialId={credentialId}
       />
       />
     );
     );
   }
   }

+ 137 - 0
dashboard/src/components/ProvisionerForm.tsx

@@ -0,0 +1,137 @@
+import React, { useEffect, useState, useContext } from "react";
+import styled from "styled-components";
+
+import api from "shared/api";
+import aws from "assets/aws.png";
+
+import { Context } from "shared/Context";
+
+import SelectRow from "components/form-components/SelectRow";
+import Heading from "components/form-components/Heading";
+import Helper from "./form-components/Helper";
+import InputRow from "./form-components/InputRow";
+import SaveButton from "./SaveButton";
+
+const regionOptions = [
+  { value: "us-east-1", label: "US East (N. Virginia) us-east-1" },
+  { value: "us-east-2", label: "US East (Ohio) us-east-2" },
+  { value: "us-west-1", label: "US West (N. California) us-west-1" },
+  { value: "us-west-2", label: "US West (Oregon) us-west-2" },
+  { value: "af-south-1", label: "Africa (Cape Town) af-south-1" },
+  { value: "ap-east-1", label: "Asia Pacific (Hong Kong) ap-east-1" },
+  { value: "ap-south-1", label: "Asia Pacific (Mumbai) ap-south-1" },
+  { value: "ap-northeast-2", label: "Asia Pacific (Seoul) ap-northeast-2" },
+  { value: "ap-southeast-1", label: "Asia Pacific (Singapore) ap-southeast-1" },
+  { value: "ap-southeast-2", label: "Asia Pacific (Sydney) ap-southeast-2" },
+  { value: "ap-northeast-1", label: "Asia Pacific (Tokyo) ap-northeast-1" },
+  { value: "ca-central-1", label: "Canada (Central) ca-central-1" },
+  { value: "eu-central-1", label: "Europe (Frankfurt) eu-central-1" },
+  { value: "eu-west-1", label: "Europe (Ireland) eu-west-1" },
+  { value: "eu-west-2", label: "Europe (London) eu-west-2" },
+  { value: "eu-south-1", label: "Europe (Milan) eu-south-1" },
+  { value: "eu-west-3", label: "Europe (Paris) eu-west-3" },
+  { value: "eu-north-1", label: "Europe (Stockholm) eu-north-1" },
+  { value: "me-south-1", label: "Middle East (Bahrain) me-south-1" },
+  { value: "sa-east-1", label: "South America (São Paulo) sa-east-1" },
+];
+
+type Props = {
+  goBack: () => void;
+  credentialId: any;
+};
+
+const ProvisionerForm: React.FC<Props> = ({
+  goBack,
+  credentialId,
+}) => {
+  const { currentProject } = useContext(Context);
+  const [createStatus, setCreateStatus] = useState("");
+  const [clusterName, setClusterName] = useState("");
+  const [awsRegion, setAwsRegion] = useState("us-east-1");
+
+  return (
+    <>
+      <Heading isAtTop>
+        <BackButton width="155px" onClick={goBack}>
+          <i className="material-icons">first_page</i>
+          Set credentials
+        </BackButton>
+        <Spacer />
+        <Img src={aws} />
+        Configure settings
+      </Heading>
+      <Helper>
+        Configure settings for your new cluster. 
+      </Helper>
+      <StyledForm>
+        <InputRow
+          width="350px"
+          isRequired
+          type="string"
+          value={clusterName}
+          setValue={(x: string) => setClusterName(x)}
+          label="🏷️ Cluster name"
+          placeholder="ex: total-perspective-vortex"
+        />
+        <SelectRow
+          options={regionOptions}
+          width="350px"
+          value={awsRegion}
+          scrollBuffer={true}
+          dropdownMaxHeight="240px"
+          setActiveValue={setAwsRegion}
+          label="📍 AWS Region"
+        />
+      </StyledForm>
+    </>
+  );
+};
+
+export default ProvisionerForm;
+
+const Spacer = styled.div`
+  height: 1px;
+  width: 17px;
+`;
+
+const Img = styled.img`
+  height: 18px;
+  margin-right: 15px;
+`;
+
+const BackButton = styled.div`
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  cursor: pointer;
+  font-size: 13px;
+  height: 35px;
+  padding: 5px 13px;
+  padding-right: 15px;
+  border: 1px solid #ffffff55;
+  border-radius: 100px;
+  width: ${(props: { width: string }) => props.width};
+  color: white;
+  background: #ffffff11;
+
+  :hover {
+    background: #ffffff22;
+  }
+
+  > i {
+    color: white;
+    font-size: 16px;
+    margin-right: 6px;
+    margin-left: -2px;
+  }
+`;
+
+const StyledForm = styled.div`
+  position: relative;
+  padding: 15px 30px 25px;
+  border-radius: 5px;
+  background: #26292e;
+  border: 1px solid #494b4f;
+  font-size: 13px;
+  margin-bottom: 30px;
+`;

+ 1 - 1
dashboard/src/components/TitleSection.tsx

@@ -57,7 +57,7 @@ const BackButton = styled.div`
   > i {
   > i {
     cursor: pointer;
     cursor: pointer;
     font-size: 24px;
     font-size: 24px;
-    color: #969fbbaa;
+    color: #aaaabb;
     margin-right: 10px;
     margin-right: 10px;
     padding: 3px;
     padding: 3px;
     margin-left: 0px;
     margin-left: 0px;

+ 8 - 43
dashboard/src/main/home/dashboard/ClusterSection.tsx

@@ -7,6 +7,7 @@ import Banner from "components/Banner";
 
 
 import ProvisionerFlow from "components/ProvisionerFlow";
 import ProvisionerFlow from "components/ProvisionerFlow";
 import ClusterPlaceholderContainer from "./ClusterPlaceholderContainer";
 import ClusterPlaceholderContainer from "./ClusterPlaceholderContainer";
+import TitleSection from "components/TitleSection";
 
 
 type Props = {
 type Props = {
 };
 };
@@ -19,13 +20,8 @@ const ClusterSection = (props: Props) => {
   if (currentStep === "cloud") {
   if (currentStep === "cloud") {
     return (
     return (
       <>
       <>
-        <Flex>
-          <BackButton width="87px" onClick={() => setCurrentStep("")}>
-            <i className="material-icons">first_page</i>
-            Back
-          </BackButton>
+        <TitleSection handleNavBack={() => setCurrentStep("")}>
           <Title>
           <Title>
-            <Flex>
             <ClusterIcon>
             <ClusterIcon>
               <svg
               <svg
                 width="19"
                 width="19"
@@ -83,10 +79,9 @@ const ClusterSection = (props: Props) => {
               </svg>
               </svg>
             </ClusterIcon>
             </ClusterIcon>
             Provision a new cluster
             Provision a new cluster
-            </Flex>
           </Title>
           </Title>
-        </Flex>
-        <Br />
+        </TitleSection>
+        <Br height="7px" />
         <Banner>
         <Banner>
           You have currently provisioned {usage?.current.cluster || "0"} out of {usage?.limit.clusters || "0"} clusters for this project.
           You have currently provisioned {usage?.current.cluster || "0"} out of {usage?.limit.clusters || "0"} clusters for this project.
         </Banner>
         </Banner>
@@ -107,9 +102,9 @@ const ClusterSection = (props: Props) => {
 
 
 export default ClusterSection;
 export default ClusterSection;
 
 
-const Br = styled.div`
+const Br = styled.div<{ height?: string }>`
   width: 100%;
   width: 100%;
-  height: 30px;
+  height: ${props => props.height || "30px"};
 `;
 `;
 
 
 const ClusterIcon = styled.div`
 const ClusterIcon = styled.div`
@@ -118,7 +113,7 @@ const ClusterIcon = styled.div`
     display: flex;
     display: flex;
     align-items: center;
     align-items: center;
     margin-bottom: -1x;
     margin-bottom: -1x;
-    margin-right: 10px;
+    margin-right: 15px;
     color: #ffffff;
     color: #ffffff;
   }
   }
 `;
 `;
@@ -127,41 +122,11 @@ const Title = styled.div`
   font-size: 20px;
   font-size: 20px;
   font-weight: 500;
   font-weight: 500;
   font-family: "Work Sans", sans-serif;
   font-family: "Work Sans", sans-serif;
-  margin-left: 15px;
+  margin-left: 5px;
   border-radius: 2px;
   border-radius: 2px;
   color: #ffffff;
   color: #ffffff;
-`;
-
-const Flex = styled.div`
-  display: flex;
-  align-items: center;
-`;
-
-const BackButton = styled.div`
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
-  justify-content: space-between;
-  cursor: pointer;
-  font-size: 13px;
-  height: 35px;
-  padding: 5px 13px;
-  padding-right: 15px;
-  border: 1px solid #ffffff55;
-  border-radius: 100px;
-  width: ${(props: { width: string }) => props.width};
-  color: white;
-  background: #ffffff11;
-
-  :hover {
-    background: #ffffff22;
-  }
-
-  > i {
-    color: white;
-    font-size: 16px;
-    margin-right: 6px;
-    margin-left: -2px;
-  }
 `;
 `;
 
 
 const Button = styled.div`
 const Button = styled.div`