فهرست منبع

create env fix

jusrhee 5 سال پیش
والد
کامیت
2a4775541c

+ 33 - 74
dashboard/src/main/home/cluster-dashboard/ClusterDashboard.tsx

@@ -10,11 +10,10 @@ import { ChartType, ClusterType } from "shared/types";
 import { PorterUrl } from "shared/routing";
 import { PorterUrl } from "shared/routing";
 
 
 import ChartList from "./chart/ChartList";
 import ChartList from "./chart/ChartList";
-import EnvGroupList from "./env-groups/EnvGroupList";
+import EnvGroupDashboard from "./env-groups/EnvGroupDashboard";
 import NamespaceSelector from "./NamespaceSelector";
 import NamespaceSelector from "./NamespaceSelector";
 import SortSelector from "./SortSelector";
 import SortSelector from "./SortSelector";
 import ExpandedChart from "./expanded-chart/ExpandedChart";
 import ExpandedChart from "./expanded-chart/ExpandedChart";
-import ExpandedEnvGroup from "./env-groups/ExpandedEnvGroup";
 import ExpandedJobChart from "./expanded-chart/ExpandedJobChart";
 import ExpandedJobChart from "./expanded-chart/ExpandedJobChart";
 import { RouteComponentProps, withRouter } from "react-router";
 import { RouteComponentProps, withRouter } from "react-router";
 
 
@@ -30,7 +29,6 @@ type StateType = {
   namespace: string;
   namespace: string;
   sortType: string;
   sortType: string;
   currentChart: ChartType | null;
   currentChart: ChartType | null;
-  expandedEnvGroup: any;
   isMetricsInstalled: boolean;
   isMetricsInstalled: boolean;
 };
 };
 
 
@@ -41,7 +39,6 @@ class ClusterDashboard extends Component<PropsType, StateType> {
       ? localStorage.getItem("SortType")
       ? localStorage.getItem("SortType")
       : "Newest",
       : "Newest",
     currentChart: null as ChartType | null,
     currentChart: null as ChartType | null,
-    expandedEnvGroup: null as any,
     isMetricsInstalled: false,
     isMetricsInstalled: false,
   };
   };
 
 
@@ -88,8 +85,6 @@ class ClusterDashboard extends Component<PropsType, StateType> {
   renderDashboardIcon = () => {
   renderDashboardIcon = () => {
     if (this.props.currentView === "jobs") {
     if (this.props.currentView === "jobs") {
       return <Img src={monojob} />;
       return <Img src={monojob} />;
-    } else if (this.props.currentView === "env-groups") {
-      return <Img src={sliders} />;
     } else {
     } else {
       return <Img src={monoweb} />;
       return <Img src={monoweb} />;
     }
     }
@@ -98,8 +93,6 @@ class ClusterDashboard extends Component<PropsType, StateType> {
   getDescription = (currentView: string): string => {
   getDescription = (currentView: string): string => {
     if (currentView === "jobs") {
     if (currentView === "jobs") {
       return "Scripts and tasks that run once or on a repeating interval.";
       return "Scripts and tasks that run once or on a repeating interval.";
-    } else if (currentView === "env-groups") {
-      return "Groups of environment variables for storing secrets and configuration."
     } else {
     } else {
       return "Continuously running web services, workers, and add-ons.";
       return "Continuously running web services, workers, and add-ons.";
     }
     }
@@ -107,64 +100,35 @@ class ClusterDashboard extends Component<PropsType, StateType> {
 
 
   renderBody = () => {
   renderBody = () => {
     let { currentCluster, setSidebar, currentView } = this.props;
     let { currentCluster, setSidebar, currentView } = this.props;
-    if (currentView === "env-groups") {
-      return (
-        <>
-          <ControlRow>
-            <Button onClick={() => this.props.history.push("launch")}>
-              <i className="material-icons">add</i> Create Env Group
-            </Button>
-            <SortFilterWrapper>
-              <SortSelector
-                setSortType={(sortType) => this.setState({ sortType })}
-                sortType={this.state.sortType}
-              />
-              <NamespaceSelector
-                setNamespace={(namespace) => this.setState({ namespace })}
-                namespace={this.state.namespace}
-              />
-            </SortFilterWrapper>
-          </ControlRow>
-
-          <EnvGroupList
-            currentCluster={currentCluster}
-            namespace={this.state.namespace}
-            sortType={this.state.sortType}
-            setExpandedEnvGroup={(envGroup: any) => this.setState({ expandedEnvGroup: envGroup })}
-          />
-        </>
-      );
-    } else {
-      return (
-        <>
-          <ControlRow>
-            <Button onClick={() => this.props.history.push("launch")}>
-              <i className="material-icons">add</i> Launch Template
-            </Button>
-            <SortFilterWrapper>
-              <SortSelector
-                setSortType={(sortType) => this.setState({ sortType })}
-                sortType={this.state.sortType}
-              />
-              <NamespaceSelector
-                setNamespace={(namespace) => this.setState({ namespace })}
-                namespace={this.state.namespace}
-              />
-            </SortFilterWrapper>
-          </ControlRow>
-
-          <ChartList
-            currentView={currentView}
-            currentCluster={currentCluster}
-            namespace={this.state.namespace}
-            sortType={this.state.sortType}
-            setCurrentChart={(x: ChartType | null) =>
-              this.setState({ currentChart: x })
-            }
-          />
-        </>
-      );
-    }
+    return (
+      <>
+        <ControlRow>
+          <Button onClick={() => this.props.history.push("launch")}>
+            <i className="material-icons">add</i> Launch Template
+          </Button>
+          <SortFilterWrapper>
+            <SortSelector
+              setSortType={(sortType) => this.setState({ sortType })}
+              sortType={this.state.sortType}
+            />
+            <NamespaceSelector
+              setNamespace={(namespace) => this.setState({ namespace })}
+              namespace={this.state.namespace}
+            />
+          </SortFilterWrapper>
+        </ControlRow>
+
+        <ChartList
+          currentView={currentView}
+          currentCluster={currentCluster}
+          namespace={this.state.namespace}
+          sortType={this.state.sortType}
+          setCurrentChart={(x: ChartType | null) =>
+            this.setState({ currentChart: x })
+          }
+        />
+      </>
+    );
   }
   }
 
 
   renderContents = () => {
   renderContents = () => {
@@ -190,14 +154,9 @@ class ClusterDashboard extends Component<PropsType, StateType> {
           setSidebar={setSidebar}
           setSidebar={setSidebar}
         />
         />
       );
       );
-    } else if (this.state.expandedEnvGroup && currentView === "env-groups") {
+    } else if (currentView === "env-groups") {
       return (
       return (
-        <ExpandedEnvGroup
-          namespace={this.state.namespace}
-          currentCluster={this.props.currentCluster}
-          initialEnvGroup={this.state.expandedEnvGroup}
-          closeExpanded={() => this.setState({ expandedEnvGroup: null })}
-        />
+        <EnvGroupDashboard currentCluster={this.props.currentCluster} />
       );
       );
     }
     }
 
 
@@ -205,7 +164,7 @@ class ClusterDashboard extends Component<PropsType, StateType> {
       <div>
       <div>
         <TitleSection>
         <TitleSection>
           {this.renderDashboardIcon()}
           {this.renderDashboardIcon()}
-          <Title>{currentView === "env-groups" ? "Environment Groups" : currentView}</Title>
+          <Title>{currentView}</Title>
         </TitleSection>
         </TitleSection>
 
 
         <InfoSection>
         <InfoSection>

+ 116 - 0
dashboard/src/main/home/cluster-dashboard/DashboardHeader.tsx

@@ -0,0 +1,116 @@
+import React, { Component } from "react";
+import styled from "styled-components";
+
+type PropsType = {
+  image: any,
+  title: string,
+  description: string,
+};
+
+type StateType = {
+};
+
+export default class DashboardHeader extends Component<PropsType, StateType> {
+  render() {
+    return (
+      <>
+        <TitleSection>
+          <Img src={this.props.image} />
+          <Title>{this.props.title}</Title>
+        </TitleSection>
+
+        <InfoSection>
+          <TopRow>
+            <InfoLabel>
+              <i className="material-icons">info</i> Info
+            </InfoLabel>
+          </TopRow>
+          <Description>
+            {this.props.description}
+          </Description>
+        </InfoSection>
+
+        <LineBreak />
+      </>
+    );
+  }
+}
+
+const Img = styled.img`
+  width: 30px;
+`;
+
+const LineBreak = styled.div`
+  width: calc(100% - 0px);
+  height: 2px;
+  background: #ffffff20;
+  margin: 10px 0px 35px;
+`;
+
+const TopRow = styled.div`
+  display: flex;
+  align-items: center;
+`;
+
+const Description = styled.div`
+  color: #aaaabb;
+  margin-top: 13px;
+  margin-left: 2px;
+  font-size: 13px;
+`;
+
+const InfoLabel = styled.div`
+  width: 72px;
+  height: 20px;
+  display: flex;
+  align-items: center;
+  color: #7a838f;
+  font-size: 13px;
+  > i {
+    color: #8b949f;
+    font-size: 18px;
+    margin-right: 5px;
+  }
+`;
+
+const InfoSection = styled.div`
+  margin-top: 20px;
+  font-family: "Work Sans", sans-serif;
+  margin-left: 0px;
+  margin-bottom: 35px;
+`;
+
+const Title = styled.div`
+  font-size: 20px;
+  font-weight: 500;
+  font-family: "Work Sans", sans-serif;
+  margin-left: 18px;
+  color: #ffffff;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  text-transform: capitalize;
+`;
+
+const TitleSection = styled.div`
+  height: 80px;
+  margin-top: 10px;
+  margin-bottom: 10px;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  padding-left: 0px;
+
+  > i {
+    margin-left: 10px;
+    cursor: pointer;
+    font-size 18px;
+    color: #858FAAaa;
+    padding: 5px;
+    border-radius: 100px;
+    :hover {
+      background: #ffffff11;
+    }
+    margin-bottom: -3px;
+  }
+`;

+ 264 - 0
dashboard/src/main/home/cluster-dashboard/env-groups/CreateEnvGroup.tsx

@@ -0,0 +1,264 @@
+import React, { Component } from "react";
+import styled from "styled-components";
+
+import sliders from "assets/sliders.svg";
+
+import { Context } from "shared/Context";
+import InputRow from "components/values-form/InputRow";
+import KeyValueArray from "components/values-form/KeyValueArray";
+import Selector from "components/Selector";
+import Helper from "components/values-form/Helper";
+import SaveButton from "components/SaveButton";
+import { isAlphanumeric } from "shared/common";
+
+type PropsType = {
+  goBack: () => void;
+};
+
+type StateType = {
+  expand: boolean;
+  update: any[];
+  envGroupName: string;
+  selectedNamespace: string;
+  namespaceOptions: any[];
+  envVariables: any;
+};
+
+export default class CreateEnvGroup extends Component<PropsType, StateType> {
+  state = {
+    expand: false,
+    update: [] as any[],
+    envGroupName: "",
+    selectedNamespace: "",
+    namespaceOptions: [] as any[],
+    envVariables: {} as any,
+  };
+
+  isDisabled = () => {
+    return !(!isAlphanumeric(this.state.envGroupName) &&
+    this.state.envGroupName !== "");
+  }
+
+  render() {
+    return (
+      <>
+        <StyledCreateEnvGroup>
+          <HeaderSection>
+            <Button onClick={this.props.goBack}>
+              <i className="material-icons">
+                keyboard_backspace
+              </i>
+              Back
+            </Button>
+            <Title>Create an Environment Group</Title>
+          </HeaderSection>
+          <DarkMatter antiHeight="-13px" />
+          <Heading isAtTop={true}>Name</Heading>
+          <Subtitle>
+            Randomly generated if left blank.
+            <Warning
+              highlight={
+                !isAlphanumeric(this.state.envGroupName) &&
+                this.state.envGroupName !== ""
+              }
+            >
+              Lowercase letters, numbers, and "-" only.
+            </Warning>
+          </Subtitle>
+          <DarkMatter antiHeight="-29px" />
+          <InputRow
+            type="text"
+            value={this.state.envGroupName}
+            setValue={(x: string) => this.setState({ envGroupName: x })}
+            placeholder="ex: doctor-scientist"
+            width="100%"
+          />
+
+          <Heading>Destination</Heading>
+          <Subtitle>
+            Specify the namespace you would like to create this environment group in.
+          </Subtitle>
+          <DestinationSection>
+            <NamespaceLabel>
+              <i className="material-icons">view_list</i>Namespace
+            </NamespaceLabel>
+            <Selector
+              key={"namespace"}
+              activeValue={this.state.selectedNamespace}
+              setActiveValue={(namespace: string) =>
+                this.setState({ selectedNamespace: namespace })
+              }
+              options={this.state.namespaceOptions}
+              width="250px"
+              dropdownWidth="335px"
+              closeOverlay={true}
+            />
+          </DestinationSection>
+
+          <Heading>Environment Variables</Heading>
+          <Helper>
+            Set environment variables for your secrets and environment-specific configuration.
+          </Helper>
+          <KeyValueArray
+            values={this.state.envVariables}
+            setValues={(x: any) => this.setState({ envVariables: x })}
+          />
+          <SaveButton
+            disabled={this.isDisabled()}
+            text="Deploy"
+            onClick={() => console.log("asdf")}
+            status={
+              this.isDisabled()
+                ? "Missing required fields"
+                : "What is the status?"
+            }
+            makeFlush={true}
+          />
+        </StyledCreateEnvGroup>
+        <Buffer />
+      </>
+    );
+  }
+}
+
+CreateEnvGroup.contextType = Context;
+
+const Buffer = styled.div`
+  width: 100%;
+  height: 150px;
+`;
+
+const StyledCreateEnvGroup = styled.div`
+  padding-bottom: 50px;
+  position: relative;
+`;
+
+const NamespaceLabel = styled.div`
+  margin-right: 10px;
+  display: flex;
+  align-items: center;
+  > i {
+    font-size: 16px;
+    margin-right: 6px;
+  }
+`;
+
+const DestinationSection = styled.div`
+  display: flex;
+  align-items: center;
+  color: #ffffff;
+  font-family: "Work Sans", sans-serif;
+  font-size: 14px;
+  margin-top: 2px;
+  font-weight: 500;
+  margin-bottom: 32px;
+
+  > i {
+    font-size: 25px;
+    color: #ffffff44;
+    margin-right: 13px;
+  }
+`;
+
+const Button = styled.div`
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  justify-content: space-between;
+  font-size: 13px;
+  cursor: pointer;
+  font-family: "Work Sans", sans-serif;
+  border-radius: 20px;
+  color: white;
+  height: 35px;
+  margin-left: -2px;
+  padding: 0px 8px;
+  padding-bottom: 1px;
+  font-weight: 500;
+  padding-right: 15px;
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+  cursor: pointer;
+  border: 2px solid #969fbbaa;
+  :hover {
+    background: #ffffff11;
+  }
+
+  > i {
+    color: white;
+    width: 18px;
+    height: 18px;
+    color: #969fbbaa;
+    font-weight: 600;
+    font-size: 14px;
+    border-radius: 20px;
+    display: flex;
+    align-items: center;
+    margin-right: 5px;
+    justify-content: center;
+  }
+`;
+
+const DarkMatter = styled.div<{ antiHeight?: string }>`
+  width: 100%;
+  margin-top: ${(props) => props.antiHeight || "-15px"};
+`;
+
+const Warning = styled.span<{ highlight: boolean; makeFlush?: boolean }>`
+  color: ${(props) => (props.highlight ? "#f5cb42" : "")};
+  margin-left: ${(props) => (props.makeFlush ? "" : "5px")};
+`;
+
+const Subtitle = styled.div`
+  padding: 11px 0px 16px;
+  font-family: "Work Sans", sans-serif;
+  font-size: 13px;
+  color: #aaaabb;
+  line-height: 1.6em;
+  display: flex;
+  align-items: center;
+`;
+
+const Title = styled.div`
+  font-size: 24px;
+  font-weight: 600;
+  font-family: "Work Sans", sans-serif;
+  margin-left: 15px;
+  border-radius: 2px;
+  color: #ffffff;
+`;
+
+const HeaderSection = styled.div`
+  display: flex;
+  align-items: center;
+  margin-bottom: 40px;
+
+  > i {
+    cursor: pointer;
+    font-size 20px;
+    color: #969Fbbaa;
+    padding: 2px;
+    border: 2px solid #969fbbaa;
+    border-radius: 100px;
+    :hover {
+      background: #ffffff11;
+    }
+  }
+
+  > img {
+    width: 20px;
+    margin-left: 17px;
+    margin-right: 7px;
+  }
+`;
+
+const Heading = styled.div<{ isAtTop?: boolean }>`
+  color: white;
+  font-weight: 500;
+  font-size: 16px;
+  margin-bottom: 5px;
+  margin-top: ${(props) => (props.isAtTop ? "10px" : "30px")};
+  display: flex;
+  align-items: center;
+`;

+ 178 - 0
dashboard/src/main/home/cluster-dashboard/env-groups/EnvGroupDashboard.tsx

@@ -0,0 +1,178 @@
+import React, { Component } from "react";
+import styled from "styled-components";
+
+import sliders from "assets/sliders.svg";
+
+import { Context } from "shared/Context";
+import { ClusterType } from "shared/types";
+
+import DashboardHeader from "../DashboardHeader";
+import NamespaceSelector from "../NamespaceSelector";
+import SortSelector from "../SortSelector";
+import EnvGroupList from "./EnvGroupList";
+import CreateEnvGroup from "./CreateEnvGroup";
+import ExpandedEnvGroup from "./ExpandedEnvGroup";
+import { RouteComponentProps, withRouter } from "react-router";
+
+type PropsType = RouteComponentProps & {
+  currentCluster: ClusterType;
+};
+
+type StateType = {
+  expand: boolean;
+  update: any[];
+  sortType: string;
+  expandedEnvGroup: any;
+  namespace: string;
+  createEnvMode: boolean;
+};
+
+class EnvGroupDashboard extends Component<PropsType, StateType> {
+  state = {
+    expand: false,
+    update: [] as any[],
+    namespace: "default",
+    expandedEnvGroup: null as any,
+    createEnvMode: false,
+    sortType: localStorage.getItem("SortType")
+      ? localStorage.getItem("SortType")
+      : "Newest",
+  };
+
+  readableDate = (s: string) => {
+    let ts = new Date(s);
+    let date = ts.toLocaleDateString();
+    let time = ts.toLocaleTimeString([], {
+      hour: "numeric",
+      minute: "2-digit",
+    });
+    return `${time} on ${date}`;
+  };
+
+  renderBody = () => {
+    if (this.state.createEnvMode) {
+      return (
+        <CreateEnvGroup goBack={() => this.setState({ createEnvMode: false })} />
+      )
+    } else {
+      return (
+        <>
+          <ControlRow>
+            <Button onClick={() => this.setState({ createEnvMode: !this.state.createEnvMode })}>
+              <i className="material-icons">add</i> Create Env Group
+            </Button>
+            <SortFilterWrapper>
+              <SortSelector
+                setSortType={(sortType) => this.setState({ sortType })}
+                sortType={this.state.sortType}
+              />
+              <NamespaceSelector
+                setNamespace={(namespace) => this.setState({ namespace })}
+                namespace={this.state.namespace}
+              />
+            </SortFilterWrapper>
+          </ControlRow>
+
+          <EnvGroupList
+            currentCluster={this.props.currentCluster}
+            namespace={this.state.namespace}
+            sortType={this.state.sortType}
+            setExpandedEnvGroup={(envGroup: any) => this.setState({ expandedEnvGroup: envGroup })}
+          />
+        </>
+      )
+    }
+  }
+
+  renderContents = () => {
+    if (this.state.expandedEnvGroup) {
+      return (
+        <ExpandedEnvGroup
+          namespace={this.state.namespace}
+          currentCluster={this.props.currentCluster}
+          initialEnvGroup={this.state.expandedEnvGroup}
+          closeExpanded={() => this.setState({ expandedEnvGroup: null })}
+        />
+      );
+    } else {
+      return (
+        <>
+          <DashboardHeader
+            image={sliders}
+            title="Environment Groups"
+            description="Groups of environment variables for storing secrets and configuration."
+          />
+          {this.renderBody()}
+        </>
+      );
+    }
+  }
+
+  render() {
+    return (
+      <>{this.renderContents()}</>
+    );
+  }
+}
+
+EnvGroupDashboard.contextType = Context;
+
+export default withRouter(EnvGroupDashboard);
+
+const SortFilterWrapper = styled.div`
+  width: 468px;
+  display: flex;
+  justify-content: space-between;
+`;
+
+const ControlRow = styled.div`
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 35px;
+  padding-left: 0px;
+`;
+
+const Button = styled.div`
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  justify-content: space-between;
+  font-size: 13px;
+  cursor: pointer;
+  font-family: "Work Sans", sans-serif;
+  border-radius: 20px;
+  color: white;
+  height: 35px;
+  padding: 0px 8px;
+  padding-bottom: 1px;
+  margin-right: 10px;
+  font-weight: 500;
+  padding-right: 15px;
+  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;
+  }
+`;