ソースを参照

Merge pull request #1151 from porter-dev/0.9.0-company-form

0.9.0 company form
jusrhee 4 年 前
コミット
e4b0cf83d7

+ 15 - 9
dashboard/package-lock.json

@@ -3090,11 +3090,12 @@
       "dev": true
     },
     "@types/react": {
-      "version": "16.14.2",
-      "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.2.tgz",
-      "integrity": "sha512-BzzcAlyDxXl2nANlabtT4thtvbbnhee8hMmH/CcJrISDBVcJS1iOsP1f0OAgSdGE0MsY9tqcrb9YoZcOFv9dbQ==",
+      "version": "16.14.14",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.14.tgz",
+      "integrity": "sha512-uwIWDYW8LznHzEMJl7ag9St1RsK0gw/xaFZ5+uI1ZM1HndwUgmPH3/wQkSb87GkOVg7shUxnpNW8DcN0AzvG5Q==",
       "requires": {
         "@types/prop-types": "*",
+        "@types/scheduler": "*",
         "csstype": "^3.0.2"
       }
     },
@@ -3162,6 +3163,11 @@
         "@types/react": "*"
       }
     },
+    "@types/scheduler": {
+      "version": "0.16.2",
+      "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
+      "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
+    },
     "@types/semver": {
       "version": "7.3.5",
       "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.5.tgz",
@@ -5638,9 +5644,9 @@
       }
     },
     "dom-helpers": {
-      "version": "5.2.0",
-      "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.0.tgz",
-      "integrity": "sha512-Ru5o9+V8CpunKnz5LGgWXkmrH/20cGKwcHwS4m73zIvs54CN9epEmT/HLqFJW3kXpakAFkEdzgy1hzlJe3E4OQ==",
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+      "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
       "requires": {
         "@babel/runtime": "^7.8.7",
         "csstype": "^3.0.2"
@@ -9306,9 +9312,9 @@
       "integrity": "sha512-jBlj70iBwOTvvImsU9t01LjFjy4sXEtclBovl3mTiqjz23Reu0DKnRza4zlLtOPACx6j2/7MrQIthIK1Wi+LIA=="
     },
     "react-transition-group": {
-      "version": "4.4.1",
-      "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz",
-      "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==",
+      "version": "4.4.2",
+      "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz",
+      "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==",
       "requires": {
         "@babel/runtime": "^7.5.5",
         "dom-helpers": "^5.0.1",

+ 2 - 1
dashboard/package.json

@@ -38,6 +38,7 @@
     "react-modal": "^3.11.2",
     "react-router-dom": "^5.2.0",
     "react-table": "^7.7.0",
+    "react-transition-group": "^4.4.2",
     "regenerator-runtime": "^0.13.9",
     "semver": "^7.3.5",
     "styled-components": "^5.2.0"
@@ -68,7 +69,7 @@
     "@types/node": "^12.12.62",
     "@types/qs": "^6.9.5",
     "@types/random-words": "^1.1.0",
-    "@types/react": "^16.9.49",
+    "@types/react": "^16.14.14",
     "@types/react-dom": "^16.9.8",
     "@types/react-modal": "^3.10.6",
     "@types/react-router": "^5.1.8",

+ 2 - 3
dashboard/src/components/porter-form/types.ts

@@ -18,7 +18,7 @@ export interface GenericInputField extends GenericField {
   settings?: any;
 
   // Read in value from Helm for existing revisions
-  value?: [any]|[];
+  value?: [any] | [];
 }
 
 export interface HeadingField extends GenericField {
@@ -61,8 +61,7 @@ export interface InputField extends GenericInputField {
 export interface CheckboxField extends GenericInputField {
   type: "checkbox";
   label?: string;
-  settings?: {
-  };
+  settings?: {};
 }
 
 export interface KeyValueArrayField extends GenericInputField {

+ 2 - 3
dashboard/src/components/porter-form/utils.ts

@@ -1,6 +1,5 @@
 import { GenericInputField } from "./types";
 
-
 export const hasSetValue = (field: GenericInputField) => {
-    return field.value && field.value.length != 0 && field.value[0] != null
-}
+  return field.value && field.value.length != 0 && field.value[0] != null;
+};

+ 10 - 0
dashboard/src/main/home/Home.tsx

@@ -12,6 +12,7 @@ import ConfirmOverlay from "components/ConfirmOverlay";
 import Loading from "components/Loading";
 import ClusterDashboard from "./cluster-dashboard/ClusterDashboard";
 import Dashboard from "./dashboard/Dashboard";
+import WelcomeForm from "./WelcomeForm";
 import Integrations from "./integrations/Integrations";
 import Templates from "./launch/Launch";
 import ClusterInstructionsModal from "./modals/ClusterInstructionsModal";
@@ -65,6 +66,7 @@ type StateType = {
 
   // Track last project id for refreshing clusters on project change
   prevProjectId: number | null;
+  showWelcomeForm: boolean;
 };
 
 // TODO: Handle cluster connected but with some failed infras (no successful set)
@@ -78,6 +80,7 @@ class Home extends Component<PropsType, StateType> {
     sidebarReady: false,
     handleDO: false,
     ghRedirect: false,
+    showWelcomeForm: true,
   };
 
   // TODO: Refactor and prevent flash + multiple reload
@@ -385,6 +388,13 @@ class Home extends Component<PropsType, StateType> {
             <Icon src={discordLogo} />
             Join Our Discord
           </DiscordButton>
+          {this.context?.capabilities?.version === "dev" &&
+            this.state.showWelcomeForm &&
+            localStorage.getItem("welcomed") != "true" && (
+              <WelcomeForm
+                closeForm={() => this.setState({ showWelcomeForm: false })}
+              />
+            )}
         </>
       );
     }

+ 305 - 0
dashboard/src/main/home/WelcomeForm.tsx

@@ -0,0 +1,305 @@
+import React, { useContext, useState } from "react";
+import styled from "styled-components";
+import { CSSTransition } from "react-transition-group";
+import axios from "axios";
+
+import { Context } from "shared/Context";
+
+type Props = {
+  closeForm: () => void;
+};
+
+type StateType = {
+  active: boolean;
+};
+
+const WelcomeForm: React.FunctionComponent<Props> = ({}) => {
+  const context = useContext(Context);
+  const [active, setActive] = useState(true);
+  const [isCompany, setIsCompany] = useState(false);
+  const [role, setRole] = useState("unspecified");
+  const [company, setCompany] = useState("");
+
+  const submitForm = () => {
+    axios
+      .get(process.env.WELCOME_FORM_WEBHOOK, {
+        params: {
+          email: context.user && context.user.email,
+          isCompany,
+          company,
+          role,
+        },
+      })
+      .then((res) => {
+        localStorage.setItem("welcomed", "true");
+        setActive(false);
+      })
+      .catch((err) => {
+        console.log(err);
+      });
+  };
+
+  const renderContents = () => {
+    if (isCompany) {
+      return (
+        <FadeWrapper>
+          <Title>Welcome to Porter</Title>
+          <Subtitle>Just two things before getting started.</Subtitle>
+          <SubtitleAlt>
+            <Num>1</Num> What is your company name? *
+          </SubtitleAlt>
+          <Input
+            placeholder="ex: Acme"
+            value={company}
+            onChange={(e: any) => setCompany(e.target.value)}
+          />
+          <SubtitleAlt>
+            <Num>2</Num> What is your role? *
+          </SubtitleAlt>
+          <RadioButton
+            onClick={() => setRole("founder")}
+            selected={role === "founder"}
+          >
+            <i className="material-icons-round">
+              {role === "founder" ? "check_box" : "check_box_outline_blank"}
+            </i>{" "}
+            Founder
+          </RadioButton>
+          <RadioButton
+            onClick={() => setRole("developer")}
+            selected={role === "developer"}
+          >
+            <i className="material-icons-round">
+              {role === "developer" ? "check_box" : "check_box_outline_blank"}
+            </i>{" "}
+            Developer
+          </RadioButton>
+          <RadioButton
+            onClick={() => setRole("devops")}
+            selected={role === "devops"}
+          >
+            <i className="material-icons-round">
+              {role === "devops" ? "check_box" : "check_box_outline_blank"}
+            </i>{" "}
+            DevOps
+          </RadioButton>
+
+          <Submit
+            isDisabled={!company || role === "unspecified"}
+            onClick={() => company && role !== "unspecified" && submitForm()}
+          >
+            <i className="material-icons-round">check</i> Done
+          </Submit>
+        </FadeWrapper>
+      );
+    }
+    return (
+      <>
+        <Title>Welcome to Porter</Title>
+        <Subtitle delay="0.7s">I am interested in using Porter as:</Subtitle>
+        <Option onClick={() => setIsCompany(true)}>
+          <i className="material-icons-round">people</i> A Company
+        </Option>
+        <Option onClick={() => submitForm()}>
+          <i className="material-icons-round">person</i> An Individual
+        </Option>
+      </>
+    );
+  };
+
+  return (
+    <CSSTransition
+      in={active}
+      timeout={500}
+      classNames="alert"
+      unmountOnExit
+      onEnter={() => setActive(true)}
+      onExited={() => setActive(false)}
+    >
+      <StyledWelcomeForm>
+        <div>
+          {renderContents()}
+          <br />
+          <br />
+        </div>
+      </StyledWelcomeForm>
+    </CSSTransition>
+  );
+};
+
+export default WelcomeForm;
+
+const Circle = styled.div`
+  width: 13px;
+  height: 13px;
+  border-radius: 20px;
+  background: #ffffff11;
+  margin-right: 12px;
+  border: 1px solid #aaaabb;
+`;
+
+const FadeWrapper = styled.div`
+  background: #202227;
+  opacity: 0;
+  animation: fadeIn 0.7s 0s;
+  animation-fill-mode: forwards;
+`;
+
+const Num = styled.div`
+  display: flex;
+  align-items: center;
+  margin-right: 15px;
+  justify-content: center;
+  width: 30px;
+  height: 30px;
+  border: 1px solid #ffffff;
+`;
+
+const Option = styled.div`
+  width: 500px;
+  max-width: 80vw;
+  height: 50px;
+  background: #ffffff22;
+  display: flex;
+  align-items: center;
+  margin-top: 15px;
+  border: 1px solid #aaaabb;
+  border-radius: 5px;
+  padding-left: 15px;
+  cursor: pointer;
+  :hover {
+    background: #ffffff44;
+  }
+
+  > i {
+    font-size: 20px;
+    margin-right: 12px;
+    color: #aaaabb;
+  }
+
+  opacity: 0;
+  animation: slideIn 0.7s 1.3s;
+  animation-fill-mode: forwards;
+
+  @keyframes slideIn {
+    from {
+      opacity: 0;
+      transform: translateX(-30px);
+    }
+    to {
+      opacity: 1;
+      transform: translateX(0);
+    }
+  }
+`;
+
+const Submit = styled(Option)<{ isDisabled: boolean }>`
+  border: 0;
+  opacity: 0;
+  animation: fadeIn 0.7s 0.5s;
+  animation-fill-mode: forwards;
+  margin-top: 35px;
+  cursor: ${(props) => (props.isDisabled ? "not-allowed" : "pointer")};
+  background: ${(props) => (props.isDisabled ? "#aaaabb" : "#616FEEcc")};
+  :hover {
+    filter: ${(props) => (props.isDisabled ? "" : "brightness(130%)")};
+    background: ${(props) => (props.isDisabled ? "#aaaabb" : "#616FEEcc")};
+  }
+
+  > i {
+    color: #ffffff;
+  }
+`;
+
+const RadioButton = styled(Option)<{ selected: boolean }>`
+  opacity: 0;
+  background: ${(props) => (props.selected ? "#ffffff44" : "#ffffff22")};
+  animation: fadeIn 0.5s 0.2s;
+  animation-fill-mode: forwards;
+
+  > div {
+    background: ${(props) => (props.selected ? "#ffffff44" : "")};
+  }
+`;
+
+const Input = styled.input`
+  width: 500px;
+  max-width: 80vw;
+  height: 50px;
+  background: #ffffff22;
+  font-size: 18px;
+  display: flex;
+  align-items: center;
+  margin-top: 0px;
+  color: #ffffff;
+  border: 1px solid #aaaabb;
+  border-radius: 5px;
+  padding-left: 15px;
+  margin-bottom: 40px;
+
+  opacity: 0;
+  animation: fadeIn 0.5s 0.2s;
+  animation-fill-mode: forwards;
+`;
+
+const Subtitle = styled.div<{ delay?: string }>`
+  margin: 20px 0 30px;
+  color: #aaaabb;
+
+  opacity: 0;
+  animation: fadeIn 0.5s ${(props) => props.delay || "0.2s"};
+  animation-fill-mode: forwards;
+`;
+
+const SubtitleAlt = styled(Subtitle)`
+  margin: -5px 0 30px;
+  color: white;
+  display: flex;
+  align-items: center;
+  animation: fadeIn 0.5s 0.2s;
+  animation-fill-mode: forwards;
+`;
+
+const Title = styled.div`
+  color: white;
+
+  font-size: 26px;
+  margin-bottom: 5px;
+  display: flex;
+  align-items: center;
+
+  opacity: 0;
+  animation: fadeIn 0.5s 0.2s;
+  animation-fill-mode: forwards;
+
+  @keyframes fadeIn {
+    from {
+      opacity: 0;
+    }
+    to {
+      opacity: 1;
+    }
+  }
+`;
+
+const StyledWelcomeForm = styled.div`
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  z-index: 999;
+  background: #202227;
+
+  &.alert-exit {
+    opacity: 1;
+  }
+  &.alert-exit-active {
+    opacity: 0;
+    transform: translateY(-100px);
+    transition: opacity 500ms, transform 1000ms;
+  }
+`;

+ 6 - 2
dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedChart.tsx

@@ -378,8 +378,12 @@ const ExpandedChart: React.FC<Props> = (props) => {
                   <Spinner src={loadingSrc} /> This application is currently
                   being deployed
                 </Header>
-                Navigate to the <A
-                  href={props.currentChart.git_action_config && `https://github.com/${props.currentChart.git_action_config?.git_repo}/actions`}
+                Navigate to the{" "}
+                <A
+                  href={
+                    props.currentChart.git_action_config &&
+                    `https://github.com/${props.currentChart.git_action_config?.git_repo}/actions`
+                  }
                   target={"_blank"}
                 >
                   Actions

+ 0 - 1
dashboard/src/main/home/navbar/Navbar.tsx

@@ -55,7 +55,6 @@ class Navbar extends Component<PropsType, StateType> {
   };
 
   renderFeedbackButton = () => {
-    console.log("hi", this.context?.capabilities)
     if (this.context?.capabilities?.provisioner) {
       return <Feedback currentView={this.props.currentView} />;
     }