Browse Source

CI repo connection flow fe

jusrhee 5 years ago
parent
commit
2102056f4a

+ 65 - 0
dashboard/src/components/RadioSelector.tsx

@@ -0,0 +1,65 @@
+import React, { Component } from "react";
+import styled from "styled-components";
+
+type PropsType = {
+  selected: string;
+  setSelected: (x: string) => void;
+  options: { value: string, label: string }[];
+};
+
+type StateType = {};
+
+export default class RadioSelector extends Component<PropsType, StateType> {
+  render() {
+    return (
+      <StyledRadioSelector>
+        {this.props.options.map((option: { label: string, value: string }, i: number) => {
+          let selected = option.value === this.props.selected;
+          return (
+            <RadioRow onClick={() => this.props.setSelected(option.value)}>
+              <Indicator selected={selected}>
+                {selected && <Circle />}
+              </Indicator>
+              {option.label}
+            </RadioRow>
+          );
+        })}
+      </StyledRadioSelector>
+    );
+  }
+}
+
+const RadioRow = styled.div`
+  display: flex;
+  align-items: center;
+  cursor: pointer;
+  margin-bottom: 12px;
+  :hover {
+    > div {
+      background: #ffffff22;
+    }
+  }
+`;
+
+const Indicator = styled.div<{ selected: boolean }>`
+  border-radius: 15px;
+  display: flex;
+  margin-right: 4px;
+  align-items: center;
+  justify-content: center;
+  width: 16px;
+  height: 16px;
+  border: 1px solid #ffffff55;
+  margin: 1px 10px 0px 1px;
+  background: ${props => props.selected ? "#ffffff22" : "#ffffff11"};
+`;
+
+const Circle = styled.div`
+  width: 8px;
+  height: 8px;
+  background: #ffffff55;
+  border-radius: 15px;
+`;
+
+const StyledRadioSelector = styled.div`
+`;

+ 105 - 26
dashboard/src/components/repo-selector/ActionConfEditor.tsx

@@ -12,11 +12,13 @@ import ActionDetails from "./ActionDetails";
 type PropsType = {
   actionConfig: ActionConfigType | null;
   branch: string;
-  pathIsSet: boolean;
   setActionConfig: (x: ActionConfigType) => void;
   setBranch: (x: string) => void;
-  setPath: (x: boolean) => void;
   reset: any;
+  dockerfilePath: string;
+  setDockerfilePath: (x: string) => void;
+  folderPath: string;
+  setFolderPath: (x: string) => void;
 };
 
 type StateType = {
@@ -24,6 +26,12 @@ type StateType = {
   error: boolean;
 };
 
+const defaultActionConfig: ActionConfigType = {
+  git_repo: "",
+  image_repo_uri: "",
+  git_repo_id: 0,
+};
+
 export default class ActionConfEditor extends Component<PropsType, StateType> {
   state = {
     loading: true,
@@ -34,10 +42,8 @@ export default class ActionConfEditor extends Component<PropsType, StateType> {
     let {
       actionConfig,
       branch,
-      pathIsSet,
       setActionConfig,
       setBranch,
-      setPath,
     } = this.props;
 
     if (!actionConfig.git_repo) {
@@ -50,7 +56,8 @@ export default class ActionConfEditor extends Component<PropsType, StateType> {
           />
         </ExpandedWrapper>
       );
-    } else if (!branch) {
+    } 
+    /* else if (!branch) {
       return (
         <>
           <ExpandedWrapperAlt>
@@ -59,10 +66,15 @@ export default class ActionConfEditor extends Component<PropsType, StateType> {
               setBranch={(branch: string) => setBranch(branch)}
             />
           </ExpandedWrapperAlt>
-          {this.renderResetButton()}
+          <Br />
+          <BackButton width="135px" onClick={() => setActionConfig({ ...defaultActionConfig })}>
+            <i className="material-icons">keyboard_backspace</i>
+            Select Repo
+          </BackButton>
         </>
       );
-    } else if (!pathIsSet) {
+    } */ 
+    else if (!this.props.dockerfilePath && !this.props.folderPath) {
       return (
         <>
           <ExpandedWrapperAlt>
@@ -70,35 +82,43 @@ export default class ActionConfEditor extends Component<PropsType, StateType> {
               actionConfig={actionConfig}
               branch={branch}
               setActionConfig={setActionConfig}
-              setPath={() => setPath(true)}
+              setDockerfilePath={(x: string) => this.props.setDockerfilePath(x)}
+              setFolderPath={(x: string) => this.props.setFolderPath(x)}
             />
           </ExpandedWrapperAlt>
-          {this.renderResetButton()}
+          <Br />
+          <BackButton width="135px" onClick={() => setActionConfig({ ...defaultActionConfig })}>
+            <i className="material-icons">keyboard_backspace</i>
+            Select Repo
+          </BackButton>
         </>
       );
     }
     return (
       <>
-        <ExpandedWrapperAlt>
-          <ActionDetails
-            actionConfig={actionConfig}
-            setActionConfig={setActionConfig}
-          />
-        </ExpandedWrapperAlt>
-        {this.renderResetButton()}
+        <ActionDetails
+          branch={branch}
+          actionConfig={actionConfig}
+          setActionConfig={setActionConfig}
+          dockerfilePath={this.props.dockerfilePath}
+          folderPath={this.props.folderPath}
+        />
+        <Flex>
+          <BackButton width="140px" onClick={() => {
+            this.props.setDockerfilePath(null);
+            this.props.setFolderPath(null);
+          }}>
+            <i className="material-icons">keyboard_backspace</i>
+            Select Folder
+          </BackButton>
+          <StatusWrapper>
+            <i className="material-icons">done</i> Source selected.
+          </StatusWrapper>
+        </Flex>
       </>
     );
   };
 
-  renderResetButton = () => {
-    return (
-      <BackButton width="150px" onClick={this.props.reset}>
-        <i className="material-icons">keyboard_backspace</i>
-        Reset Selection
-      </BackButton>
-    );
-  };
-
   render() {
     return <>{this.renderExpanded()}</>;
   }
@@ -106,6 +126,62 @@ export default class ActionConfEditor extends Component<PropsType, StateType> {
 
 ActionConfEditor.contextType = Context;
 
+const StatusWrapper = styled.div`
+  display: flex;
+  align-items: center;
+  font-family: "Work Sans", sans-serif;
+  font-size: 13px;
+  color: #ffffff55;
+  margin-right: 25px;
+  margin-left: 20px;
+  margin-top: 26px;
+
+  > i {
+    font-size: 18px;
+    margin-right: 10px;
+    color: #4797ff;
+  }
+
+  animation: statusFloatIn 0.5s;
+  animation-fill-mode: forwards;
+
+  @keyframes statusFloatIn {
+    from {
+      opacity: 0;
+      transform: translateY(10px);
+    }
+    to {
+      opacity: 1;
+      transform: translateY(0px);
+    }
+  }
+`;
+
+const Br = styled.div`
+  width: 100%;
+  height: 8px;
+`;
+
+const Flex = styled.div`
+  display: flex;
+  align-items: center;  
+`;
+
+const HeaderButton = styled.div`
+  margin-bottom: 5px;
+  padding: 5px 10px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-weight: 500;
+  margin-right: 10px;
+`;
+
+const RepoHeader = styled.div`
+  display: flex;
+  align-items: center;
+`;
+
 const ExpandedWrapper = styled.div`
   margin-top: 10px;
   width: 100%;
@@ -121,10 +197,13 @@ const BackButton = styled.div`
   display: flex;
   align-items: center;
   justify-content: space-between;
-  margin-top: 10px;
+  margin-top: 22px;
   cursor: pointer;
   font-size: 13px;
+  height: 35px;
   padding: 5px 13px;
+  margin-bottom: -7px;
+  padding-right: 15px;
   border: 1px solid #ffffff55;
   border-radius: 3px;
   width: ${(props: { width: string }) => props.width};

+ 37 - 54
dashboard/src/components/repo-selector/ActionDetails.tsx

@@ -9,6 +9,9 @@ import InputRow from "../values-form/InputRow";
 type PropsType = {
   actionConfig: ActionConfigType | null;
   setActionConfig: (x: ActionConfigType) => void;
+  branch: string;
+  dockerfilePath: string;
+  folderPath: string;
 };
 
 type StateType = {
@@ -22,74 +25,54 @@ export default class ActionDetails extends Component<PropsType, StateType> {
     error: false,
   };
 
-  componentDidMount() {
-    if (this.props.actionConfig.dockerfile_path) {
-      this.setPath("/Dockerfile");
-    } else {
-      this.setPath("Dockerfile");
-    }
-  }
-
-  setPath = (x: string) => {
-    let { actionConfig, setActionConfig } = this.props;
-    let updatedConfig = actionConfig;
-    updatedConfig.dockerfile_path = updatedConfig.dockerfile_path.concat(x);
-    setActionConfig(updatedConfig);
-  };
-
-  setURL = (x: string) => {
-    let { actionConfig, setActionConfig } = this.props;
-    let updatedConfig = actionConfig;
-    updatedConfig.image_repo_uri = x;
-    setActionConfig(updatedConfig);
-  };
-
-  renderConfirmation = () => {
+  render() {
     return (
-      <Holder>
+      <>
+        <DarkMatter />
         <InputRow
           disabled={true}
           label="Git Repository"
           type="text"
           width="100%"
           value={this.props.actionConfig.git_repo}
-          setValue={(x: string) => console.log(x)}
-        />
-        <InputRow
-          disabled={true}
-          label="Dockerfile Path"
-          type="text"
-          width="100%"
-          value={this.props.actionConfig.dockerfile_path}
-          setValue={(x: string) => console.log(x)}
-        />
-        <Label>Target Image URL</Label>
-        <ImageSelector
-          selectedTag="latest"
-          selectedImageUrl={this.props.actionConfig.image_repo_uri}
-          setSelectedImageUrl={this.setURL}
-          setSelectedTag={() => null}
-          forceExpanded={true}
-          noTagSelection={true}
         />
-      </Holder>
+        {
+          this.props.dockerfilePath ? (
+            <InputRow
+              disabled={true}
+              label="Dockerfile Path"
+              type="text"
+              width="100%"
+              value={this.props.dockerfilePath}
+            />
+          ) : (
+            <InputRow
+              disabled={true}
+              label="Folder Path"
+              type="text"
+              width="100%"
+              value={this.props.folderPath}
+            />
+          )
+        }
+        <Br />
+      </>
     );
-  };
-
-  render() {
-    return <div>{this.renderConfirmation()}</div>;
   }
 }
 
-const Label = styled.div`
-  color: #ffffff;
-  display: flex;
-  align-items: center;
-  font-size: 13px;
-  font-family: "Work Sans", sans-serif;
+ActionDetails.contextType = Context;
+
+const Br = styled.div`
+  width: 100%;
+  height: 1px;
+  margin-bottom: -8px;
 `;
 
-ActionDetails.contextType = Context;
+const DarkMatter = styled.div`
+  width: 100%;
+  margin-bottom: -18px;
+`;
 
 const Holder = styled.div`
   padding: 0px 12px 24px 12px;

+ 20 - 1
dashboard/src/components/repo-selector/BranchList.tsx

@@ -1,6 +1,7 @@
 import React, { Component } from "react";
 import styled from "styled-components";
 import branch_icon from "assets/branch.png";
+import info from "assets/info.svg";
 
 import api from "../../shared/api";
 import { Context } from "../../shared/Context";
@@ -79,7 +80,17 @@ export default class BranchList extends Component<PropsType, StateType> {
   };
 
   render() {
-    return <div>{this.renderBranchList()}</div>;
+    return (
+      <>
+        <InfoRow
+          lastItem={false}
+        >
+          <img src={info} />
+          Select Branch
+        </InfoRow>
+        {this.renderBranchList()}
+      </>
+    );
   }
 }
 
@@ -114,6 +125,14 @@ const BranchName = styled.div`
   }
 `;
 
+const InfoRow = styled(BranchName)`
+  cursor: default;
+  color: #ffffff55;
+  :hover {
+    background: #ffffff11;
+  }
+`;
+
 const LoadingWrapper = styled.div`
   padding: 30px 0px;
   background: #ffffff11;

+ 0 - 158
dashboard/src/components/repo-selector/ButtonTray.tsx

@@ -1,158 +0,0 @@
-import React, { Component } from "react";
-import styled from "styled-components";
-
-import api from "../../shared/api";
-import { ActionConfigType } from "../../shared/types";
-import { Context } from "../../shared/Context";
-
-type PropsType = {
-  chartName: string | null;
-  chartNamespace: string | null;
-  pathIsSet: boolean;
-  branch: string;
-  actionConfig: ActionConfigType | null;
-  setBranch: (x: string) => void;
-  setActionConfig: (x: ActionConfigType) => void;
-  setPath: (x: boolean) => void;
-};
-
-type StateType = {};
-
-export default class RepoSelector extends Component<PropsType, StateType> {
-  createGHAction = () => {
-    let { currentProject, currentCluster } = this.context;
-    let { actionConfig, chartName, chartNamespace } = this.props;
-
-    api
-      .createGHAction(
-        "<token>",
-        {
-          git_repo: actionConfig.git_repo,
-          image_repo_uri: actionConfig.image_repo_uri,
-          dockerfile_path: actionConfig.dockerfile_path,
-          git_repo_id: actionConfig.git_repo_id,
-        },
-        {
-          project_id: currentProject.id,
-          CLUSTER_ID: currentCluster.id,
-          RELEASE_NAME: chartName,
-          RELEASE_NAMESPACE: chartNamespace,
-        }
-      )
-      .then((res) => console.log(res.data))
-      .catch(console.log);
-  };
-
-  setSelectedRepo = () => {
-    let { actionConfig, setActionConfig } = this.props;
-    let updatedConfig = actionConfig;
-    updatedConfig.git_repo = "";
-    updatedConfig.git_repo_id = null as number;
-    setActionConfig(updatedConfig);
-  };
-
-  goToBranchSelect = () => {
-    let { actionConfig, setActionConfig, setBranch } = this.props;
-    let updatedConfig = actionConfig;
-    updatedConfig.dockerfile_path = "";
-    setBranch("");
-    setActionConfig(updatedConfig);
-  };
-
-  goToPathSelect = () => {
-    let { actionConfig, setActionConfig, setPath } = this.props;
-    let updatedConfig = actionConfig;
-    updatedConfig.image_repo_uri = "";
-    updatedConfig.dockerfile_path = updatedConfig.dockerfile_path.slice(0, -11);
-    setPath(false);
-    setActionConfig(updatedConfig);
-  };
-
-  renderExpanded = () => {
-    let { actionConfig, pathIsSet, branch } = this.props;
-
-    if (!actionConfig.git_repo) {
-      return <></>;
-    } else if (!branch) {
-      return (
-        <ButtonTray>
-          <BackButton width="130px" onClick={() => this.setSelectedRepo()}>
-            <i className="material-icons">keyboard_backspace</i>
-            Select Repo
-          </BackButton>
-        </ButtonTray>
-      );
-    } else if (!pathIsSet) {
-      return (
-        <ButtonTray>
-          <BackButton onClick={() => this.goToBranchSelect()} width="140px">
-            <i className="material-icons">keyboard_backspace</i>
-            Select Branch
-          </BackButton>
-        </ButtonTray>
-      );
-    }
-    return (
-      <ButtonTray>
-        <BackButton width="130px" onClick={() => this.goToPathSelect()}>
-          <i className="material-icons">keyboard_backspace</i>
-          Select Dockerfile
-        </BackButton>
-        <BackButton
-          disabled={
-            !actionConfig.git_repo ||
-            !actionConfig.dockerfile_path ||
-            !actionConfig.image_repo_uri
-          }
-          width="146px"
-          onClick={() => this.createGHAction()}
-        >
-          <i className="material-icons">local_shipping</i>
-          Create Github Action
-        </BackButton>
-      </ButtonTray>
-    );
-  };
-
-  render() {
-    return <>{this.renderExpanded()}</>;
-  }
-}
-
-RepoSelector.contextType = Context;
-
-const BackButton = styled.div`
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  margin-top: 10px;
-  cursor: pointer;
-  font-size: 13px;
-  padding: 5px 10px;
-  border: 1px solid #ffffff55;
-  border-radius: 3px;
-  width: ${(props: { width: string; disabled?: boolean }) => props.width};
-  color: ${(props: { width: string; disabled?: boolean }) =>
-    props.disabled ? "#ffffff55" : "white"};
-  pointer-events: ${(props: { width: string; disabled?: boolean }) =>
-    props.disabled ? "none" : "auto"};
-
-  :hover {
-    background: #ffffff11;
-  }
-
-  > i {
-    color: ${(props: { width: string; disabled?: boolean }) =>
-      props.disabled ? "#ffffff55" : "white"};
-    font-size: 18px;
-    margin-right: 10px;
-  }
-`;
-
-const ButtonTray = styled.div`
-  margin-top: 10px;
-  display: flex;
-  flex-direction: row;
-  justify-content: space-between;
-  align-items: center;
-`;

+ 219 - 23
dashboard/src/components/repo-selector/ContentsList.tsx

@@ -3,6 +3,7 @@ import styled from "styled-components";
 import file from "assets/file.svg";
 import folder from "assets/folder.svg";
 import info from "assets/info.svg";
+import close from "assets/close.png";
 
 import api from "../../shared/api";
 import { Context } from "../../shared/Context";
@@ -14,39 +15,51 @@ type PropsType = {
   actionConfig: ActionConfigType | null;
   branch: string;
   setActionConfig: (x: ActionConfigType) => void;
-  setPath: () => void;
+  setDockerfilePath: (x: string) => void;
+  setFolderPath: (x: string) => void;
 };
 
 type StateType = {
   loading: boolean;
   error: boolean;
   contents: FileType[];
+  currentDir: string;
+  dockerfiles: string[];
 };
 
+const dummyDockerfiles = [
+  "dev.Dockerfile",
+  "prod.Dockerfile",
+  "Dockerfile",
+]
+
 export default class ContentsList extends Component<PropsType, StateType> {
   state = {
     loading: true,
     error: false,
     contents: [] as FileType[],
+    currentDir: "",
+    dockerfiles: [] as string[],
   };
 
-  setSubdirectory = (x: string) => {
-    let { actionConfig, setActionConfig } = this.props;
-    let updatedConfig = actionConfig;
-    updatedConfig.dockerfile_path = x;
-    setActionConfig(updatedConfig);
+  componentDidMount() {
     this.updateContents();
+  }
+
+  setSubdirectory = (x: string) => {
+    this.setState({ currentDir: x }, () => this.updateContents());
+    
   };
 
   updateContents = () => {
-    let { actionConfig, branch } = this.props;
     let { currentProject } = this.context;
-
+    let { actionConfig, branch } = this.props;
+    console.log(this.state.currentDir)
     // Get branch contents
     api
       .getBranchContents(
         "<token>",
-        { dir: actionConfig.dockerfile_path },
+        { dir: this.state.currentDir },
         {
           project_id: currentProject.id,
           git_repo_id: actionConfig.git_repo_id,
@@ -80,10 +93,6 @@ export default class ContentsList extends Component<PropsType, StateType> {
       });
   };
 
-  componentDidMount() {
-    this.updateContents();
-  }
-
   renderContentList = () => {
     let { contents, loading, error } = this.state;
     if (loading) {
@@ -103,7 +112,7 @@ export default class ContentsList extends Component<PropsType, StateType> {
         return (
           <Item
             key={i}
-            isSelected={item.Path === this.props.actionConfig.dockerfile_path}
+            isSelected={item.Path === this.state.currentDir}
             lastItem={i === contents.length - 1}
             onClick={() => this.setSubdirectory(item.Path)}
           >
@@ -113,13 +122,13 @@ export default class ContentsList extends Component<PropsType, StateType> {
         );
       }
 
-      if (fileName === "Dockerfile") {
+      if (fileName.includes("Dockerfile")) {
         return (
           <FileItem
             key={i}
             lastItem={i === contents.length - 1}
             isADocker
-            onClick={() => this.props.setPath()}
+            onClick={() => this.props.setDockerfilePath(item.Path)}
           >
             <img src={file} />
             {fileName}
@@ -136,12 +145,11 @@ export default class ContentsList extends Component<PropsType, StateType> {
   };
 
   renderJumpToParent = () => {
-    let { actionConfig } = this.props;
-    if (actionConfig.dockerfile_path !== "") {
-      let splits = actionConfig.dockerfile_path.split("/");
+    if (this.state.currentDir !== "") { 
+      let splits = this.state.currentDir.split("/");
       let subdir = "";
       if (splits.length !== 1) {
-        subdir = actionConfig.dockerfile_path.replace(
+        subdir = this.state.currentDir.replace(
           splits[splits.length - 1],
           ""
         );
@@ -160,23 +168,211 @@ export default class ContentsList extends Component<PropsType, StateType> {
     return (
       <FileItem lastItem={false}>
         <img src={info} />
-        Select path to Dockerfile
+        Select Application Folder
       </FileItem>
     );
   };
 
+  handleContinue = () => {
+    let dockerfiles = [] as string[];
+    this.state.contents.forEach((item: FileType, i: number) => {
+      let splits = item.Path.split("/");
+      let fileName = splits[splits.length - 1];
+      if (fileName.includes("Dockerfile")) {
+        dockerfiles.push(fileName);
+      }
+    });
+    this.setState({ dockerfiles });
+  }
+
+  renderOverlay = () => {
+    if (this.state.dockerfiles.length > 0) {
+      return (
+        <Overlay>
+          <BgOverlay onClick={() => this.setState({ dockerfiles: [] })} />
+          <CloseButton onClick={() => this.setState({ dockerfiles: [] })}>
+            <CloseButtonImg src={close} />
+          </CloseButton>
+          <Label>
+            Porter has detected at least one Dockerfile in this folder. Would you like to use an existing Dockerfile?
+          </Label>
+          <DockerfileList>
+            {this.state.dockerfiles.map((dockerfile: string, i: number) => {
+              return (
+                <Row
+                  onClick={() => this.props.setDockerfilePath(`${this.state.currentDir}/${dockerfile}`)} 
+                  isLast={this.state.dockerfiles.length - 1 === i}
+                >
+                  <Indicator selected={false}>
+                  </Indicator>
+                  {dockerfile}
+                </Row>
+              );
+            })}
+          </DockerfileList>
+          <ConfirmButton onClick={() => this.props.setFolderPath(this.state.currentDir)}>
+            Skip
+          </ConfirmButton>
+        </Overlay>
+      );
+    }
+  }
+
   render() {
     return (
-      <div>
+      <>
         {this.renderJumpToParent()}
         {this.renderContentList()}
-      </div>
+        <UseButton onClick={this.handleContinue}>
+          Continue
+        </UseButton>
+        {this.renderOverlay()}
+      </>
     );
   }
 }
 
 ContentsList.contextType = Context;
 
+const BgOverlay = styled.div`
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  top: 0;
+  left: 0;
+  background-color: rgba(0, 0, 0, 0.8);
+  z-index: -1;
+`;
+
+const CloseButton = styled.div`
+  position: absolute;
+  display: block;
+  width: 40px;
+  height: 40px;
+  padding: 13px 0 12px 0;
+  z-index: 1;
+  text-align: center;
+  border-radius: 50%;
+  right: 15px;
+  top: 12px;
+  cursor: pointer;
+  :hover {
+    background-color: #ffffff11;
+  }
+`;
+
+const CloseButtonImg = styled.img`
+  width: 14px;
+  margin: 0 auto;
+`;
+
+const Indicator = styled.div<{ selected: boolean }>`
+  border-radius: 15px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 16px;
+  height: 16px;
+  border: 1px solid #ffffff55;
+  margin: 1px 10px 0px 1px;
+  margin-right: 13px;
+  background: ${props => props.selected ? "#ffffff22" : "#ffffff11"};
+`;
+
+const Label = styled.div`
+  max-width: 420px;
+  line-height: 1.5em;
+  text-align: center;
+  font-size: 14px;
+`;
+
+const DockerfileList = styled.div`
+  border-radius: 3px;
+  margin-top: 20px;
+  border: 1px solid #aaaabb;
+  background: #ffffff22;
+  width: 100%;
+  max-width: 500px;
+  max-height: 140px;
+  overflow-y: auto;
+`;
+
+const Row = styled.div<{ isLast: boolean }>`
+  height: 35px;
+  padding-left: 10px;
+  display: flex;
+  align-items: center;
+  border-bottom: ${props => !props.isLast && "1px solid #aaaabb"};
+  cursor: pointer;
+  :hover {
+    background: #ffffff22;
+  }
+`;
+
+const ConfirmButton = styled.div`
+  font-size: 18px;
+  padding: 7px 12px;
+  outline: none;
+  border: 1px solid white;
+  margin-top: 25px;
+  border-radius: 10px;
+  text-align: center;
+  cursor: pointer;
+  opacity: 0;
+  font-family: "Work Sans", sans-serif;
+  font-size: 14px;
+  font-weight: 500;
+  animation: linEnter 0.3s 0.1s;
+  animation-fill-mode: forwards;
+  @keyframes linEnter {
+    from {
+      transform: translateY(20px);
+      opacity: 0;
+    }
+    to {
+      transform: translateY(0px);
+      opacity: 1;
+    }
+  }
+  :hover {
+    background: white;
+    color: #232323;
+  }
+`;
+
+const Overlay = styled.div`
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 999;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-direction: column;
+  padding: 0 90px;
+`;
+
+const UseButton = styled.div`
+  position: absolute;
+  bottom: 28px;
+  left: 185px;
+  height: 35px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background: #616FEEcc;
+  font-weight: 500;
+  padding: 10px 15px;
+  border-radius: 3px;
+  box-shadow: 0 2px 5px 0 #00000030;
+  cursor: pointer;
+  :hover {
+    filter: brightness(120%);
+  }
+`;
+
 const BackLabel = styled.div`
   font-size: 16px;
   padding-left: 16px;

+ 33 - 2
dashboard/src/components/repo-selector/RepoList.tsx

@@ -1,6 +1,7 @@
 import React, { Component } from "react";
 import styled from "styled-components";
 import github from "assets/github.png";
+import info from "assets/info.svg";
 
 import api from "shared/api";
 import { RepoType, ActionConfigType } from "shared/types";
@@ -51,6 +52,15 @@ export default class ActionConfEditor extends Component<PropsType, StateType> {
                   repo.GHRepoID = grid;
                 });
                 allRepos = allRepos.concat(res.data);
+                allRepos.sort((a: any, b: any) => {
+                  if (a.FullName < b.FullName) {
+                    return -1;
+                  } else if (a.FullName > b.FullName) {
+                    return 1;
+                  } else {
+                    return 0;
+                  }
+                });
                 this.setState({
                   repos: allRepos,
                   loading: false,
@@ -79,6 +89,7 @@ export default class ActionConfEditor extends Component<PropsType, StateType> {
           res.data.forEach((repo: any, id: number) => {
             repo.GHRepoID = grid;
           });
+          // TODO: sort repos alphabetically
           this.setState({ repos: res.data, loading: false, error: false });
         })
         .catch((err) => {
@@ -139,9 +150,21 @@ export default class ActionConfEditor extends Component<PropsType, StateType> {
 
   renderExpanded = () => {
     if (this.props.readOnly) {
-      return <ExpandedWrapperAlt>{this.renderRepoList()}</ExpandedWrapperAlt>;
+      return <ExpandedWrapperAlt>asdffdas{this.renderRepoList()}</ExpandedWrapperAlt>;
     } else {
-      return <ExpandedWrapper>{this.renderRepoList()}</ExpandedWrapper>;
+      return (
+        <ExpandedWrapper>
+          <InfoRow
+            isSelected={false}
+            lastItem={false}
+            readOnly={this.props.readOnly}
+          >
+            <img src={info} />
+            Select Repo
+          </InfoRow>
+          {this.renderRepoList()}
+        </ExpandedWrapper>
+      );
     }
   };
 
@@ -194,6 +217,14 @@ const RepoName = styled.div`
   }
 `;
 
+const InfoRow = styled(RepoName)`
+  cursor: default;
+  color: #ffffff55;
+  :hover {
+    background: #ffffff11;
+  }
+`;
+
 const LoadingWrapper = styled.div`
   padding: 30px 0px;
   background: #ffffff11;

+ 0 - 177
dashboard/src/components/repo-selector/RepoSelector.tsx

@@ -1,177 +0,0 @@
-import React, { Component } from "react";
-import styled from "styled-components";
-import github from "assets/github.png";
-import info from "assets/info.svg";
-import { RepoType, ChartType, ActionConfigType } from "shared/types";
-import { Context } from "shared/Context";
-
-import ButtonTray from "./ButtonTray";
-import ActionConfEditor from "./ActionConfEditor";
-
-type PropsType = {
-  chart: ChartType | null;
-  forceExpanded?: boolean;
-  actionConfig: ActionConfigType | null;
-  setActionConfig: (x: ActionConfigType) => void;
-  resetActionConfig: () => void;
-};
-
-type StateType = {
-  isExpanded: boolean;
-  repos: RepoType[];
-  branch: string;
-  pathIsSet: boolean;
-  dockerfileSelected: boolean;
-};
-
-export default class RepoSelector extends Component<PropsType, StateType> {
-  state = {
-    isExpanded: this.props.forceExpanded,
-    repos: [] as RepoType[],
-    branch: "",
-    pathIsSet: false,
-    dockerfileSelected: false,
-  };
-
-  renderExpanded = () => {
-    let { actionConfig, setActionConfig, chart } = this.props;
-
-    return (
-      <div>
-        <ActionConfEditor
-          actionConfig={actionConfig}
-          branch={this.state.branch}
-          pathIsSet={this.state.pathIsSet}
-          setActionConfig={setActionConfig}
-          setBranch={(branch: string) => this.setState({ branch })}
-          setPath={(pathIsSet: boolean) => this.setState({ pathIsSet })}
-          reset={() => {
-            this.setState({
-              branch: "",
-              pathIsSet: false,
-              dockerfileSelected: false,
-            });
-            this.props.resetActionConfig();
-          }}
-        />
-        <ButtonTray
-          chartName={chart.name}
-          chartNamespace={chart.namespace}
-          pathIsSet={this.state.pathIsSet}
-          branch={this.state.branch}
-          actionConfig={actionConfig}
-          setBranch={(branch: string) => this.setState({ branch })}
-          setActionConfig={setActionConfig}
-          setPath={(pathIsSet: boolean) => this.setState({ pathIsSet })}
-        />
-      </div>
-    );
-  };
-
-  renderSelected = () => {
-    let { actionConfig } = this.props;
-    if (actionConfig.git_repo) {
-      let subdir =
-        actionConfig.dockerfile_path === ""
-          ? ""
-          : "/" + actionConfig.dockerfile_path;
-      return (
-        <RepoLabel>
-          <img src={github} />
-          {actionConfig.git_repo + subdir}
-          <SelectedBranch>
-            {!this.state.branch ? "(Select Branch)" : this.state.branch}
-          </SelectedBranch>
-        </RepoLabel>
-      );
-    }
-    return (
-      <RepoLabel>
-        <img src={info} />
-        No source selected
-      </RepoLabel>
-    );
-  };
-
-  handleClick = () => {
-    if (!this.props.forceExpanded) {
-      this.setState({ isExpanded: !this.state.isExpanded });
-    }
-  };
-
-  render() {
-    return (
-      <>
-        <StyledRepoSelector
-          onClick={this.handleClick}
-          isExpanded={this.state.isExpanded}
-          forceExpanded={this.props.forceExpanded}
-        >
-          {this.renderSelected()}
-          {this.props.forceExpanded ? null : (
-            <i className="material-icons">
-              {this.state.isExpanded ? "close" : "build"}
-            </i>
-          )}
-        </StyledRepoSelector>
-
-        {this.state.isExpanded ? this.renderExpanded() : null}
-      </>
-    );
-  }
-}
-
-RepoSelector.contextType = Context;
-
-const SelectedBranch = styled.div`
-  color: #ffffff55;
-  margin-left: 10px;
-`;
-
-const RepoLabel = styled.div`
-  display: flex;
-  align-items: center;
-
-  > img {
-    width: 18px;
-    height: 18px;
-    margin-left: 12px;
-    margin-right: 12px;
-  }
-`;
-
-const StyledRepoSelector = styled.div`
-  width: 100%;
-  margin-top: 22px;
-  border: 1px solid #ffffff55;
-  background: ${(props: { isExpanded: boolean; forceExpanded: boolean }) =>
-    props.isExpanded ? "#ffffff11" : ""};
-  border-radius: 3px;
-  user-select: none;
-  height: 40px;
-  font-size: 13px;
-  color: #ffffff;
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  cursor: ${(props: { isExpanded: boolean; forceExpanded: boolean }) =>
-    props.forceExpanded ? "" : "pointer"};
-  :hover {
-    background: #ffffff11;
-
-    > i {
-      background: #ffffff22;
-    }
-  }
-
-  > i {
-    font-size: 16px;
-    color: #ffffff66;
-    margin-right: 10px;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    border-radius: 20px;
-    padding: 4px;
-  }
-`;

+ 1 - 1
dashboard/src/components/values-form/InputRow.tsx

@@ -5,7 +5,7 @@ type PropsType = {
   label?: string;
   type: string;
   value: string | number;
-  setValue: (x: string | number) => void;
+  setValue?: (x: string | number) => void;
   unit?: string;
   placeholder?: string;
   width?: string;

+ 0 - 110
dashboard/src/main/home/cluster-dashboard/expanded-chart/SettingsSection.tsx

@@ -12,7 +12,6 @@ import {
 import { Context } from "shared/Context";
 
 import ImageSelector from "components/image-selector/ImageSelector";
-import RepoSelector from "components/repo-selector/RepoSelector";
 import SaveButton from "components/SaveButton";
 import Heading from "components/values-form/Heading";
 import Helper from "components/values-form/Helper";
@@ -25,7 +24,6 @@ type PropsType = {
 };
 
 type StateType = {
-  actionConfig: ActionConfigType;
   sourceType: string;
   selectedImageUrl: string | null;
   selectedTag: string | null;
@@ -36,17 +34,8 @@ type StateType = {
   action: ActionConfigType;
 };
 
-// TODO: put in shared, duped from LaunchTemplate.tsx
-const defaultActionConfig: ActionConfigType = {
-  git_repo: "",
-  image_repo_uri: "",
-  git_repo_id: 0,
-  dockerfile_path: "",
-};
-
 export default class SettingsSection extends Component<PropsType, StateType> {
   state = {
-    actionConfig: defaultActionConfig,
     sourceType: "",
     selectedImageUrl: "",
     selectedTag: "",
@@ -58,7 +47,6 @@ export default class SettingsSection extends Component<PropsType, StateType> {
       git_repo: "",
       image_repo_uri: "",
       git_repo_id: 0,
-      dockerfile_path: "",
     } as ActionConfigType,
   };
 
@@ -136,104 +124,6 @@ export default class SettingsSection extends Component<PropsType, StateType> {
       });
   };
 
-  /*
-    <Helper>
-      Specify a container image and tag or
-      <Highlight onClick={() => this.setState({ sourceType: 'repo' })}>
-        link a repo
-      </Highlight>.
-    </Helper>
-  */
-  renderSourceSection = () => {
-    if (!this.props.currentChart.form.hasSource) {
-      return;
-    }
-
-    if (this.state.action.git_repo.length > 0) {
-      return (
-        <>
-          <Heading>Connected Source</Heading>
-          <Holder>
-            <InputRow
-              disabled={true}
-              label="Git Repository"
-              type="text"
-              width="100%"
-              value={this.state.action.git_repo}
-              setValue={(x: string) => console.log(x)}
-            />
-            <InputRow
-              disabled={true}
-              label="Dockerfile Path"
-              type="text"
-              width="100%"
-              value={this.state.action.dockerfile_path}
-              setValue={(x: string) => console.log(x)}
-            />
-            <InputRow
-              disabled={true}
-              label="Docker Image Repository"
-              type="text"
-              width="100%"
-              value={this.state.action.image_repo_uri}
-              setValue={(x: string) => console.log(x)}
-            />
-          </Holder>
-        </>
-      );
-    }
-
-    if (this.state.sourceType === "registry") {
-      return (
-        <>
-          <Heading>Connected Source</Heading>
-          <Helper>Specify a container image and tag.</Helper>
-          <ImageSelector
-            selectedImageUrl={this.state.selectedImageUrl}
-            selectedTag={this.state.selectedTag}
-            setSelectedImageUrl={(x: string) =>
-              this.setState({ selectedImageUrl: x })
-            }
-            setSelectedTag={(x: string) => this.setState({ selectedTag: x })}
-            forceExpanded={true}
-          />
-        </>
-      );
-    }
-
-    let { currentProject } = this.context;
-    return (
-      <>
-        <Heading>Connect a Source</Heading>
-        <Helper>
-          Select a repo to connect to. You can
-          <A
-            padRight={true}
-            href={`/api/oauth/projects/${currentProject.id}/github?redirected=true`}
-          >
-            log in with GitHub
-          </A>{" "}
-          or
-          <Highlight onClick={() => this.setState({ sourceType: "registry" })}>
-            link an image registry
-          </Highlight>
-          .
-        </Helper>
-        <RepoSelector
-          chart={this.props.currentChart}
-          forceExpanded={true}
-          actionConfig={this.state.actionConfig}
-          setActionConfig={(actionConfig: ActionConfigType) =>
-            this.setState({ actionConfig })
-          }
-          resetActionConfig={() =>
-            this.setState({ actionConfig: defaultActionConfig })
-          }
-        />
-      </>
-    );
-  };
-
   renderWebhookSection = () => {
     if (!this.props.currentChart.form.hasSource) {
       return;

+ 41 - 20
dashboard/src/main/home/launch/expanded-template/LaunchTemplate.tsx

@@ -21,6 +21,7 @@ import SaveButton from "components/SaveButton";
 import ActionConfEditor from "components/repo-selector/ActionConfEditor";
 import ValuesWrapper from "components/values-form/ValuesWrapper";
 import ValuesForm from "components/values-form/ValuesForm";
+import RadioSelector from "components/RadioSelector";
 import { isAlphanumeric } from "shared/common";
 
 type PropsType = {
@@ -48,14 +49,15 @@ type StateType = {
   namespaceOptions: { label: string; value: string }[];
   actionConfig: ActionConfigType;
   branch: string;
-  pathIsSet: boolean;
+  repoType: string;
+  dockerfilePath: string | null;
+  folderPath: string | null;
 };
 
 const defaultActionConfig: ActionConfigType = {
   git_repo: "",
   image_repo_uri: "",
   git_repo_id: 0,
-  dockerfile_path: "",
 };
 
 export default class LaunchTemplate extends Component<PropsType, StateType> {
@@ -76,7 +78,9 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
     namespaceOptions: [] as { label: string; value: string }[],
     actionConfig: { ...defaultActionConfig },
     branch: "",
-    pathIsSet: false,
+    repoType: "",
+    dockerfilePath: null as string | null,
+    folderPath: null as string | null,
   };
 
   createGHAction = (chartName: string, chartNamespace: string) => {
@@ -88,8 +92,8 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
         "<token>",
         {
           git_repo: actionConfig.git_repo,
-          image_repo_uri: actionConfig.image_repo_uri,
-          dockerfile_path: actionConfig.dockerfile_path,
+          registry_id: 1,
+          dockerfile_path: this.state.dockerfilePath,
           git_repo_id: actionConfig.git_repo_id,
         },
         {
@@ -440,9 +444,7 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
           >
             <BlockIcon src="https://3.bp.blogspot.com/-xhNpNJJyQhk/XIe4GY78RQI/AAAAAAAAItc/ouueFUj2Hqo5dntmnKqEaBJR4KQ4Q2K3ACK4BGAYYCw/s1600/logo%2Bgit%2Bicon.png" />
             <BlockTitle>Git Repository</BlockTitle>
-            <BlockDescription>
-              Deploy using source from a Git repo.
-            </BlockDescription>
+            <BlockDescription>Deploy using source from a Git repo.</BlockDescription>
           </Block>
           <Block
             onClick={() => {
@@ -451,9 +453,7 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
           >
             <BlockIcon src="https://cdn4.iconfinder.com/data/icons/logos-and-brands/512/97_Docker_logo_logos-512.png" />
             <BlockTitle>Docker Registry</BlockTitle>
-            <BlockDescription>
-              Deploy an already containerized application.
-            </BlockDescription>
+            <BlockDescription>Deploy a container from an image registry.</BlockDescription>
           </Block>
         </BlockList>
       );
@@ -464,8 +464,7 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
             <CloseButtonImg src={close} />
           </CloseButton>
           <Subtitle>
-            Specify the container image you would like to connect to this
-            template.
+            Specify the container image you would like to connect to this template.
             <Required>*</Required>
           </Subtitle>
           <DarkMatter antiHeight="-4px" />
@@ -479,6 +478,26 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
           <br />
         </StyledSourceBox>
       );
+    } else if (this.state.repoType === "" && false) {
+      return (
+        <StyledSourceBox>
+          <CloseButton onClick={() => this.setState({ sourceType: "" })}>
+            <CloseButtonImg src={close} />
+          </CloseButton>
+          <Subtitle>
+            Are you using an existing Dockerfile from your repo?
+            <Required>*</Required>
+          </Subtitle>
+          <RadioSelector
+            options={[
+              { value: "dockerfile", label: "Yes, I am using an existing Dockerfile" },
+              { value: "buildpack", label: "No, I am not using an existing Dockerfile" }
+            ]}
+            selected={this.state.repoType}
+            setSelected={(x: string) => this.setState({ repoType: x })}
+          />
+        </StyledSourceBox>
+      );
     } else {
       return (
         <StyledSourceBox>
@@ -486,14 +505,13 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
             <CloseButtonImg src={close} />
           </CloseButton>
           <Subtitle>
-            Select a repo to connect to, then a Dockerfile to build from.
+            Provide a repo folder to use as source.
             <Required>*</Required>
           </Subtitle>
           <DarkMatter antiHeight="-4px" />
           <ActionConfEditor
             actionConfig={this.state.actionConfig}
             branch={this.state.branch}
-            pathIsSet={this.state.pathIsSet}
             setActionConfig={(actionConfig: ActionConfigType) =>
               this.setState({ actionConfig }, () => {
                 this.setSelectedImageUrl(
@@ -502,12 +520,16 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
               })
             }
             setBranch={(branch: string) => this.setState({ branch })}
-            setPath={(pathIsSet: boolean) => this.setState({ pathIsSet })}
+            setDockerfilePath={(x: string) => this.setState({ dockerfilePath: x })}
+            dockerfilePath={this.state.dockerfilePath}
+            folderPath={this.state.folderPath}
+            setFolderPath={(x: string) => this.setState({ folderPath: x })}
             reset={() => {
               this.setState({
                 actionConfig: { ...defaultActionConfig },
                 branch: "",
-                pathIsSet: false,
+                dockerfilePath: null,
+                folderPath: null,
               });
             }}
           />
@@ -522,12 +544,11 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
       <>
         <Heading>Deployment Method</Heading>
         <Subtitle>
-          Choose the deployment method you would like to use for this
-          application.
+          Choose the deployment method you would like to use for this application.
         </Subtitle>
         {this.renderSourceSelectorContent()}
       </>
-    );
+    )
   };
 
   render() {

+ 1 - 1
dashboard/src/shared/api.tsx

@@ -98,7 +98,7 @@ const createGCR = baseApi<
 const createGHAction = baseApi<
   {
     git_repo: string;
-    image_repo_uri: string;
+    registry_id: number;
     dockerfile_path: string;
     git_repo_id: number;
   },

+ 0 - 1
dashboard/src/shared/types.tsx

@@ -165,5 +165,4 @@ export interface ActionConfigType {
   git_repo: string;
   image_repo_uri: string;
   git_repo_id: number;
-  dockerfile_path: string;
 }