Browse Source

Added error validation to project name

jnfrati 4 years ago
parent
commit
689b599f1b

+ 7 - 1
dashboard/src/components/SaveButton.tsx

@@ -16,6 +16,9 @@ type Props = {
   makeFlush?: boolean;
   clearPosition?: boolean;
   statusPosition?: "right" | "left";
+  // Provide the classname to modify styles from other components
+  className?: string;
+  successText?: string;
 };
 
 const SaveButton: React.FC<Props> = (props) => {
@@ -25,7 +28,9 @@ const SaveButton: React.FC<Props> = (props) => {
         return (
           <StatusWrapper position={props.statusPosition} successful={true}>
             <i className="material-icons">done</i>
-            <StatusTextWrapper>Successfully updated</StatusTextWrapper>
+            <StatusTextWrapper>
+              {props?.successText || "Successfully updated"}
+            </StatusTextWrapper>
           </StatusWrapper>
         );
       } else if (props.status === "loading") {
@@ -65,6 +70,7 @@ const SaveButton: React.FC<Props> = (props) => {
     <ButtonWrapper
       makeFlush={props.makeFlush}
       clearPosition={props.clearPosition}
+      className={props.className}
     >
       {props.statusPosition !== "right" && <div>{renderStatus()}</div>}
       <Button

+ 64 - 51
dashboard/src/main/home/onboarding/NewProject.tsx

@@ -1,4 +1,4 @@
-import React, { useContext, useEffect } from "react";
+import React, { useContext, useEffect, useState } from "react";
 import styled from "styled-components";
 
 import gradient from "assets/gradient.png";
@@ -12,11 +12,18 @@ import { actions, OnboardingState } from "./OnboardingState";
 import { useRouting } from "shared/routing";
 import { Context } from "shared/Context";
 import api from "shared/api";
+import SaveButton from "components/SaveButton";
+
+type ValidationError = {
+  hasError: boolean;
+  description?: string;
+};
 
 export const NewProjectFC = () => {
   const snap = useSnapshot(OnboardingState);
   const { user, setProjects, setCurrentProject } = useContext(Context);
   const { pushFiltered } = useRouting();
+  const [buttonStatus, setButtonStatus] = useState("");
 
   useEffect(() => {
     if (snap.userId !== null) {
@@ -26,8 +33,43 @@ export const NewProjectFC = () => {
     }
   }, [snap.userId]);
 
+  const validateProjectName = (): ValidationError => {
+    const name = snap.projectName;
+    if (name === "") {
+      return {
+        hasError: true,
+        description: "The name cannot be empty. Please fill the input.",
+      };
+    }
+    if (!isAlphanumeric(name)) {
+      return {
+        hasError: true,
+        description:
+          'Please be sure that the text is alphanumeric. (lowercase letters, numbers, and "-" only)',
+      };
+    }
+    if (name.length > 25) {
+      return {
+        hasError: true,
+        description:
+          "The length of the name cannot be more than 25 characters.",
+      };
+    }
+
+    return {
+      hasError: false,
+    };
+  };
+
   const createProject = async () => {
     const { projectName } = snap;
+    setButtonStatus("loading");
+    const validation = validateProjectName();
+
+    if (validation.hasError) {
+      setButtonStatus(validation.description);
+      return;
+    }
 
     try {
       const project = await api
@@ -47,7 +89,11 @@ export const NewProjectFC = () => {
         .then((res) => res.data);
       setProjects(projectList);
       setCurrentProject(project);
+
+      pushFiltered("/onboarding/provision", []);
+      setButtonStatus("success");
     } catch (error) {
+      setButtonStatus("Couldn't create project, try again.");
       console.log(error);
     }
   };
@@ -57,11 +103,7 @@ export const NewProjectFC = () => {
       <TitleSection>New Project</TitleSection>
       <Helper>
         Project name
-        <Warning
-          highlight={
-            !isAlphanumeric(snap.projectName) && snap.projectName !== ""
-          }
-        >
+        <Warning highlight={validateProjectName().hasError}>
           (lowercase letters, numbers, and "-" only) 25 letters max length
         </Warning>
         <Required>*</Required>
@@ -76,32 +118,33 @@ export const NewProjectFC = () => {
         <InputRow
           type="string"
           value={snap.projectName}
-          setValue={(x: string) => actions.setProjectName(x)}
+          setValue={(x: string) => {
+            setButtonStatus("");
+            actions.setProjectName(x);
+          }}
           placeholder="ex: perspective-vortex"
           width="470px"
-          maxLength={25}
         />
       </InputWrapper>
-      <SaveButton
-        text="Submit"
-        disabled={!isAlphanumeric(projectName)}
-        onClick={this.onSkip}
+      <NewProjectSaveButton
+        text="Create Project"
+        disabled={false}
+        onClick={createProject}
         status={buttonStatus}
         makeFlush={true}
-        helper="Note: Provisioning can take up to 15 minutes"
+        clearPosition={true}
+        statusPosition="right"
+        saveText="Creating project..."
+        successText="Project created successfully!"
       />
-      <NextButton
-        disabled={false}
-        onClick={() => {
-          pushFiltered("/onboarding/provision", []);
-        }}
-      >
-        Create Project
-      </NextButton>
     </>
   );
 };
 
+const NewProjectSaveButton = styled(SaveButton)`
+  margin-top: 24px;
+`;
+
 const Required = styled.div`
   margin-left: 8px;
   color: #fc4976;
@@ -153,33 +196,3 @@ const Warning = styled.span`
   margin-left: ${(props: { highlight: boolean; makeFlush?: boolean }) =>
     props.makeFlush ? "" : "5px"};
 `;
-
-const NextButton = styled.button<{ disabled: boolean }>`
-  height: 35px;
-  font-size: 13px;
-  font-weight: 500;
-  font-family: "Work Sans", sans-serif;
-  color: white;
-  display: flex;
-  align-items: center;
-  padding: 0 15px;
-  margin-top: 13px;
-  text-align: left;
-  float: left;
-  margin-left: 0;
-  justify-content: center;
-  border: 0;
-  border-radius: 5px;
-  background: ${(props) => (!props.disabled ? "#616FEEcc" : "#aaaabb")};
-  box-shadow: ${(props) =>
-    !props.disabled ? "0 2px 5px 0 #00000030" : "none"};
-  cursor: ${(props) => (!props.disabled ? "pointer" : "default")};
-  user-select: none;
-  :focus {
-    outline: 0;
-  }
-  :hover {
-    filter: ${(props) => (!props.disabled ? "brightness(120%)" : "")};
-  }
-  margin-bottom: 10px;
-`;

+ 1 - 11
dashboard/src/main/home/onboarding/OnboardingState.ts

@@ -1,3 +1,4 @@
+import { isAlphanumeric } from "shared/common";
 import { ContextProps } from "shared/types";
 import { proxy } from "valtio";
 import { derive, devtools } from "valtio/utils";
@@ -15,17 +16,6 @@ export const OnboardingState = proxy<OnboardingStateType>({
   userId: null,
 });
 
-derive(
-  {
-    isProjectNameValid(get) {
-      return get(OnboardingState).projectName;
-    },
-  },
-  {
-    proxy: OnboardingState,
-  }
-);
-
 devtools(OnboardingState, "Onboarding state");
 
 export const actions = {