Procházet zdrojové kódy

Merge pull request #392 from porter-dev/beta.3.buildpack-ci

Beta.3.buildpack ci
abelanger5 před 5 roky
rodič
revize
e2ac3bd3b0
34 změnil soubory, kde provedl 506 přidání a 373 odebrání
  1. 1 1
      dashboard/src/components/InfoTooltip.tsx
  2. 3 3
      dashboard/src/components/ResourceTab.tsx
  3. 5 4
      dashboard/src/components/SaveButton.tsx
  4. 2 2
      dashboard/src/components/Selector.tsx
  5. 7 4
      dashboard/src/components/StatusIndicator.tsx
  6. 1 1
      dashboard/src/components/TabRegion.tsx
  7. 1 1
      dashboard/src/components/TooltipParent.tsx
  8. 42 3
      dashboard/src/components/repo-selector/ActionDetails.tsx
  9. 35 4
      dashboard/src/components/repo-selector/ContentsList.tsx
  10. 4 1
      dashboard/src/components/values-form/ValuesForm.tsx
  11. 2 2
      dashboard/src/main/CurrentError.tsx
  12. 11 8
      dashboard/src/main/Login.tsx
  13. 5 5
      dashboard/src/main/Main.tsx
  14. 11 8
      dashboard/src/main/Register.tsx
  15. 1 1
      dashboard/src/main/home/Home.tsx
  16. 56 46
      dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedChart.tsx
  17. 15 15
      dashboard/src/main/home/cluster-dashboard/expanded-chart/metrics/AreaChart.tsx
  18. 149 126
      dashboard/src/main/home/cluster-dashboard/expanded-chart/metrics/MetricsSection.tsx
  19. 10 7
      dashboard/src/main/home/cluster-dashboard/expanded-chart/status/ControllerTab.tsx
  20. 1 1
      dashboard/src/main/home/cluster-dashboard/expanded-chart/status/Logs.tsx
  21. 20 12
      dashboard/src/main/home/launch/Launch.tsx
  22. 9 10
      dashboard/src/main/home/launch/expanded-template/ExpandedTemplate.tsx
  23. 7 3
      dashboard/src/main/home/launch/expanded-template/LaunchTemplate.tsx
  24. 1 1
      dashboard/src/main/home/launch/expanded-template/TemplateInfo.tsx
  25. 1 1
      dashboard/src/main/home/launch/hardcodedNameDict.tsx
  26. 2 2
      dashboard/src/shared/Context.tsx
  27. 3 3
      dashboard/src/shared/ansiparser.tsx
  28. 68 65
      dashboard/src/shared/api.tsx
  29. 6 6
      dashboard/src/shared/baseApi.tsx
  30. 17 17
      dashboard/src/shared/common.tsx
  31. 5 5
      dashboard/src/shared/feedback.tsx
  32. 2 2
      dashboard/src/shared/rosettaStone.tsx
  33. 2 2
      dashboard/src/shared/routing.tsx
  34. 1 1
      dashboard/src/shared/types.tsx

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

@@ -11,7 +11,7 @@ type StateType = {
 
 export default class InfoTooltip extends Component<PropsType, StateType> {
   state = {
-    showTooltip: false
+    showTooltip: false,
   };
 
   render() {

+ 3 - 3
dashboard/src/components/ResourceTab.tsx

@@ -26,7 +26,7 @@ type StateType = {
 export default class ResourceTab extends Component<PropsType, StateType> {
   state = {
     expanded: this.props.expanded || false,
-    showTooltip: false
+    showTooltip: false,
   };
 
   renderDropdownIcon = () => {
@@ -95,7 +95,7 @@ export default class ResourceTab extends Component<PropsType, StateType> {
       handleClick,
       selected,
       status,
-      roundAllCorners
+      roundAllCorners,
     } = this.props;
     return (
       <StyledResourceTab
@@ -254,7 +254,7 @@ const StatusColor = styled.div`
       : props.status === "failed" || props.status === "FailedValidation"
       ? "#ed5f85"
       : props.status === "completed"
-      ? '#00d12a'
+      ? "#00d12a"
       : "#f5cb42"};
   border-radius: 20px;
 `;

+ 5 - 4
dashboard/src/components/SaveButton.tsx

@@ -132,14 +132,15 @@ const Button = styled.button`
   text-align: left;
   border: 0;
   border-radius: 5px;
-  background: ${props => (!props.disabled ? props.color : "#aaaabb")};
-  box-shadow: ${props => (!props.disabled ? "0 2px 5px 0 #00000030" : "none")};
-  cursor: ${props => (!props.disabled ? "pointer" : "default")};
+  background: ${(props) => (!props.disabled ? props.color : "#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%)" : "")};
+    filter: ${(props) => (!props.disabled ? "brightness(120%)" : "")};
   }
 `;

+ 2 - 2
dashboard/src/components/Selector.tsx

@@ -17,7 +17,7 @@ type StateType = {};
 
 export default class Selector extends Component<PropsType, StateType> {
   state = {
-    expanded: false
+    expanded: false,
   };
 
   wrapperRef: any = React.createRef();
@@ -192,7 +192,7 @@ const Dropdown = styled.div`
 
 const StyledSelector = styled.div<{ width: string }>`
   position: relative;
-  width: ${props => props.width};
+  width: ${(props) => props.width};
 `;
 
 const MainSelector = styled.div`

+ 7 - 4
dashboard/src/components/StatusIndicator.tsx

@@ -34,8 +34,11 @@ export default class StatusIndicator extends Component<PropsType, StateType> {
         let value = this.props.controllers[uid];
         let available = this.getAvailability(value.metadata.kind, value);
 
-        if (value.metadata.kind?.toLowerCase() === "job" && !value.status?.active) {
-          return "completed"
+        if (
+          value.metadata.kind?.toLowerCase() === "job" &&
+          !value.status?.active
+        ) {
+          return "completed";
         }
 
         let progressing = true;
@@ -73,7 +76,7 @@ export default class StatusIndicator extends Component<PropsType, StateType> {
       case "daemonset":
         return c.status.numberAvailable == c.status.desiredNumberScheduled;
       case "job":
-        return c.status.active
+        return c.status.active;
     }
   };
 
@@ -105,7 +108,7 @@ const StatusColor = styled.div`
       : props.status === "failed"
       ? "#ed5f85"
       : props.status === "completed"
-      ? '#00d12a'
+      ? "#00d12a"
       : "#f5cb42"};
   border-radius: 20px;
   margin-right: 16px;

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

@@ -27,7 +27,7 @@ export default class TabRegion extends Component<PropsType, StateType> {
   componentDidUpdate(prevProps: PropsType) {
     let { options, currentTab } = this.props;
     if (prevProps.options !== options) {
-      if (options.filter(x => x.value === currentTab).length === 0) {
+      if (options.filter((x) => x.value === currentTab).length === 0) {
         this.props.setCurrentTab(this.defaultTab());
       }
     }

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

@@ -11,7 +11,7 @@ type StateType = {
 
 export default class TooltipParent extends Component<PropsType, StateType> {
   state = {
-    showTooltip: false
+    showTooltip: false,
   };
 
   renderTooltip = (): JSX.Element | undefined => {

+ 42 - 3
dashboard/src/components/repo-selector/ActionDetails.tsx

@@ -80,7 +80,10 @@ export default class ActionDetails extends Component<PropsType, StateType> {
       return (
         <RegistryItem
           key={i}
-          isSelected={this.props.selectedRegistry && registry.id === this.props.selectedRegistry.id}
+          isSelected={
+            this.props.selectedRegistry &&
+            registry.id === this.props.selectedRegistry.id
+          }
           lastItem={i === registries.length - 1}
           onClick={() => this.props.setSelectedRegistry(registry)}
         >
@@ -137,6 +140,16 @@ export default class ActionDetails extends Component<PropsType, StateType> {
           />
         )}
         {this.renderRegistrySection()}
+        <SubtitleAlt>
+          <Bold>Note:</Bold> To auto-deploy each time you push changes, Porter
+          will write Github Secrets and a GitHub Actions file to your repo.
+          <Highlight
+            href="https://docs.getporter.dev/docs/auto-deploy-requirements#cicd-with-github-actions"
+            target="_blank"
+          >
+            Learn More
+          </Highlight>
+        </SubtitleAlt>
         <Br />
 
         <Flex>
@@ -152,11 +165,11 @@ export default class ActionDetails extends Component<PropsType, StateType> {
           </BackButton>
           {this.props.selectedRegistry ? (
             <StatusWrapper successful={true}>
-              <i className="material-icons">done</i> Source selected.
+              <i className="material-icons">done</i> Source selected
             </StatusWrapper>
           ) : (
             <StatusWrapper>
-              <i className="material-icons">error_outline</i> A connected
+              <i className="material-icons">error_outline</i>A connected
               container registry is required
             </StatusWrapper>
           )}
@@ -168,6 +181,19 @@ export default class ActionDetails extends Component<PropsType, StateType> {
 
 ActionDetails.contextType = Context;
 
+const Highlight = styled.a`
+  color: #949eff;
+  text-decoration: none;
+  margin-left: 5px;
+  cursor: pointer;
+`;
+
+const Bold = styled.div`
+  font-weight: 800;
+  color: #ffffff;
+  margin-right: 5px;
+`;
+
 const Required = styled.div`
   margin-left: 8px;
   color: #fc4976;
@@ -178,6 +204,19 @@ const Subtitle = styled.div`
   margin-top: 21px;
 `;
 
+const SubtitleAlt = 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;
+  margin-top: -3px;
+  margin-bottom: -7px;
+  font-weight: 400;
+`;
+
 const RegistryItem = styled.div`
   display: flex;
   width: 100%;

+ 35 - 4
dashboard/src/components/repo-selector/ContentsList.tsx

@@ -232,7 +232,16 @@ export default class ContentsList extends Component<PropsType, StateType> {
       <>
         {this.renderJumpToParent()}
         {this.renderContentList()}
-        <UseButton onClick={this.handleContinue}>Continue</UseButton>
+        <FlexWrapper>
+          <UseButton onClick={this.handleContinue}>Continue</UseButton>
+          <StatusWrapper
+            href="https://docs.getporter.dev/docs/auto-deploy-requirements#auto-build-with-cloud-native-buildpacks"
+            target="_blank"
+          >
+            <i className="material-icons">help_outline</i>
+            <div>Auto build requirements</div>
+          </StatusWrapper>
+        </FlexWrapper>
         {this.renderOverlay()}
       </>
     );
@@ -241,6 +250,31 @@ export default class ContentsList extends Component<PropsType, StateType> {
 
 ContentsList.contextType = Context;
 
+const FlexWrapper = styled.div`
+  position: absolute;
+  bottom: 28px;
+  left: 185px;
+  display: flex;
+  align-items: center;
+`;
+
+const StatusWrapper = styled.a<{ successful?: boolean }>`
+  display: flex;
+  align-items: center;
+  font-family: "Work Sans", sans-serif;
+  font-size: 13px;
+  color: #949eff;
+  margin-right: 25px;
+  margin-left: 20px;
+  cursor: pointer;
+  text-decoration: none;
+
+  > i {
+    font-size: 18px;
+    margin-right: 8px;
+  }
+`;
+
 const BgOverlay = styled.div`
   position: absolute;
   width: 100%;
@@ -362,9 +396,6 @@ const Overlay = styled.div`
 `;
 
 const UseButton = styled.div`
-  position: absolute;
-  bottom: 28px;
-  left: 185px;
   height: 35px;
   display: flex;
   align-items: center;

+ 4 - 1
dashboard/src/components/values-form/ValuesForm.tsx

@@ -82,7 +82,10 @@ export default class ValuesForm extends Component<PropsType, StateType> {
                 this.props.setMetaState({ [key]: x });
 
                 // Need to pull env vars out of form.yaml for createGHA build env vars
-                if (this.props.handleEnvChange && key === "container.env.normal") {
+                if (
+                  this.props.handleEnvChange &&
+                  key === "container.env.normal"
+                ) {
                   this.props.handleEnvChange(x);
                 }
               }}

+ 2 - 2
dashboard/src/main/CurrentError.tsx

@@ -12,7 +12,7 @@ type StateType = {};
 
 export default class CurrentError extends Component<PropsType, StateType> {
   state = {
-    expanded: false
+    expanded: false,
   };
 
   componentDidUpdate(prevProps: PropsType) {
@@ -32,7 +32,7 @@ export default class CurrentError extends Component<PropsType, StateType> {
           <StyledCurrentError onClick={() => this.setState({ expanded: true })}>
             <ErrorText>Error: {this.props.currentError}</ErrorText>
             <CloseButton
-              onClick={e => {
+              onClick={(e) => {
                 this.context.setCurrentError(null);
                 e.stopPropagation();
               }}

+ 11 - 8
dashboard/src/main/Login.tsx

@@ -23,7 +23,7 @@ export default class Login extends Component<PropsType, StateType> {
     email: "",
     password: "",
     emailError: false,
-    credentialError: false
+    credentialError: false,
   };
 
   handleKeyDown = (e: any) => {
@@ -57,11 +57,11 @@ export default class Login extends Component<PropsType, StateType> {
           "",
           {
             email: email,
-            password: password
+            password: password,
           },
           {}
         )
-        .then(res => {
+        .then((res) => {
           // TODO: case and set credential error
           if (res?.data?.redirect) {
             window.location.href = res.data.redirect;
@@ -70,7 +70,7 @@ export default class Login extends Component<PropsType, StateType> {
             authenticate();
           }
         })
-        .catch(err =>
+        .catch((err) =>
           this.context.setCurrentError(err.response.data.errors[0])
         );
     }
@@ -137,7 +137,7 @@ export default class Login extends Component<PropsType, StateType> {
                   this.setState({
                     email: e.target.value,
                     emailError: false,
-                    credentialError: false
+                    credentialError: false,
                   })
                 }
                 valid={!credentialError && !emailError}
@@ -152,7 +152,7 @@ export default class Login extends Component<PropsType, StateType> {
                 onChange={(e: ChangeEvent<HTMLInputElement>) =>
                   this.setState({
                     password: e.target.value,
-                    credentialError: false
+                    credentialError: false,
                   })
                 }
                 valid={!credentialError}
@@ -169,8 +169,11 @@ export default class Login extends Component<PropsType, StateType> {
         </LoginPanel>
 
         <Footer>
-          © 2021 Porter Technologies Inc. • 
-          <Link href="https://docs.getporter.dev/docs/terms-of-service" target="_blank">
+          © 2021 Porter Technologies Inc. •
+          <Link
+            href="https://docs.getporter.dev/docs/terms-of-service"
+            target="_blank"
+          >
             Terms & Privacy
           </Link>
         </Footer>

+ 5 - 5
dashboard/src/main/Main.tsx

@@ -24,7 +24,7 @@ export default class Main extends Component<PropsType, StateType> {
   state = {
     loading: true,
     isLoggedIn: false,
-    initialized: localStorage.getItem("init") === "true"
+    initialized: localStorage.getItem("init") === "true",
   };
 
   componentDidMount() {
@@ -34,19 +34,19 @@ export default class Main extends Component<PropsType, StateType> {
     error && setCurrentError(error);
     api
       .checkAuth("", {}, {})
-      .then(res => {
+      .then((res) => {
         if (res && res.data) {
           setUser(res?.data?.id, res?.data?.email);
           this.setState({
             isLoggedIn: true,
             initialized: true,
-            loading: false
+            loading: false,
           });
         } else {
           this.setState({ isLoggedIn: false, loading: false });
         }
       })
-      .catch(err => this.setState({ isLoggedIn: false, loading: false }));
+      .catch((err) => this.setState({ isLoggedIn: false, loading: false }));
   }
 
   initialize = () => {
@@ -106,7 +106,7 @@ export default class Main extends Component<PropsType, StateType> {
         />
         <Route
           path={`/:baseRoute`}
-          render={routeProps => {
+          render={(routeProps) => {
             const baseRoute = routeProps.match.params.baseRoute;
             if (
               this.state.isLoggedIn &&

+ 11 - 8
dashboard/src/main/Register.tsx

@@ -25,7 +25,7 @@ export default class Register extends Component<PropsType, StateType> {
     password: "",
     confirmPassword: "",
     emailError: false,
-    confirmPasswordError: false
+    confirmPasswordError: false,
   };
 
   handleKeyDown = (e: any) => {
@@ -66,7 +66,7 @@ export default class Register extends Component<PropsType, StateType> {
           "",
           {
             email: email,
-            password: password
+            password: password,
           },
           {}
         )
@@ -78,7 +78,7 @@ export default class Register extends Component<PropsType, StateType> {
             authenticate();
           }
         })
-        .catch(err => setCurrentError(err.response.data.errors[0]));
+        .catch((err) => setCurrentError(err.response.data.errors[0]));
     }
   };
 
@@ -112,7 +112,7 @@ export default class Register extends Component<PropsType, StateType> {
       password,
       confirmPassword,
       emailError,
-      confirmPasswordError
+      confirmPasswordError,
     } = this.state;
 
     return (
@@ -154,7 +154,7 @@ export default class Register extends Component<PropsType, StateType> {
               onChange={(e: ChangeEvent<HTMLInputElement>) =>
                 this.setState({
                   password: e.target.value,
-                  confirmPasswordError: false
+                  confirmPasswordError: false,
                 })
               }
               valid={true}
@@ -167,7 +167,7 @@ export default class Register extends Component<PropsType, StateType> {
                 onChange={(e: ChangeEvent<HTMLInputElement>) =>
                   this.setState({
                     confirmPassword: e.target.value,
-                    confirmPasswordError: false
+                    confirmPasswordError: false,
                   })
                 }
                 valid={!confirmPasswordError}
@@ -183,8 +183,11 @@ export default class Register extends Component<PropsType, StateType> {
           </FormWrapper>
         </LoginPanel>
         <Footer>
-          © 2021 Porter Technologies Inc. • 
-          <Link href="https://docs.getporter.dev/docs/terms-of-service" target="_blank">
+          © 2021 Porter Technologies Inc. •
+          <Link
+            href="https://docs.getporter.dev/docs/terms-of-service"
+            target="_blank"
+          >
             Terms & Privacy
           </Link>
         </Footer>

+ 1 - 1
dashboard/src/main/home/Home.tsx

@@ -202,7 +202,7 @@ class Home extends Component<PropsType, StateType> {
     // Handle redirect from DO
     let queryString = window.location.search;
     let urlParams = new URLSearchParams(queryString);
-    
+
     window.analytics.identify(user.userId, {
       email: user.email,
       createdAt: Date.now(),

+ 56 - 46
dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedChart.tsx

@@ -288,9 +288,16 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
       case "metrics":
         return <MetricsSection currentChart={chart} />;
       case "status":
-        let controller_uid = Object.keys(this.state.controllers)[0]
+        let controller_uid = Object.keys(this.state.controllers)[0];
         if (chart.chart.metadata.name == "job") {
-          return <StatusSection currentChart={chart} selectors={[`job-name=${chart.name}-job,controller-uid=${controller_uid}`]} />;          
+          return (
+            <StatusSection
+              currentChart={chart}
+              selectors={[
+                `job-name=${chart.name}-job,controller-uid=${controller_uid}`,
+              ]}
+            />
+          );
         }
         return <StatusSection currentChart={chart} />;
       case "settings":
@@ -374,19 +381,13 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
     }
 
     // Append universal tabs
-    tabOptions.push(
-      { label: "Status", value: "status" },
-    );
+    tabOptions.push({ label: "Status", value: "status" });
 
     if (this.props.isMetricsInstalled) {
-      tabOptions.push(
-        { label: "Metrics", value: "metrics" },
-      )
+      tabOptions.push({ label: "Metrics", value: "metrics" });
     }
 
-    tabOptions.push(
-      { label: "Chart Overview", value: "graph" }
-    );
+    tabOptions.push({ label: "Chart Overview", value: "graph" });
 
     if (this.state.devOpsMode) {
       tabOptions.push(
@@ -458,7 +459,10 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
         let value = this.state.controllers[uid];
         let available = this.getAvailability(value.metadata.kind, value);
 
-        if (value.metadata.kind?.toLowerCase() == "job" && !value.status?.active) {
+        if (
+          value.metadata.kind?.toLowerCase() == "job" &&
+          !value.status?.active
+        ) {
           return "completed";
         }
 
@@ -497,7 +501,7 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
       case "daemonset":
         return c.status.numberAvailable == c.status.desiredNumberScheduled;
       case "job":
-        return c.status.active
+        return c.status.active;
     }
   };
 
@@ -530,41 +534,45 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
           revision: currentChart.version,
         }
       )
-      .then((res) => this.setState({ components: res.data.Objects }, () => {
-        let ingressName = null;
-        for (var i = 0; i < this.state.components.length; i++) {
-          if (this.state.components[i].Kind === "Ingress") {
-            ingressName = this.state.components[i].Name;
+      .then((res) =>
+        this.setState({ components: res.data.Objects }, () => {
+          let ingressName = null;
+          for (var i = 0; i < this.state.components.length; i++) {
+            if (this.state.components[i].Kind === "Ingress") {
+              ingressName = this.state.components[i].Name;
+            }
           }
-        }
 
-      api
-        .getIngress(
-          "<token>",
-          {
-            cluster_id: currentCluster.id,
-          },
-          {
-            id: currentProject.id,
-            name: ingressName,
-            namespace: `${this.props.currentChart.namespace}`,
-          }
-        )
-        .then((res) => {
-          if (res.data?.spec?.rules && res.data?.spec?.rules[0]?.host) {
-            this.setState({ url: `https://${res.data?.spec?.rules[0]?.host}` });
-            return;
-          }
-  
-          if (res.data?.status?.loadBalancer?.ingress) {
-            this.setState({
-              url: `http://${res.data?.status?.loadBalancer?.ingress[0]?.hostname}`,
-            });
-            return;
-          }
+          api
+            .getIngress(
+              "<token>",
+              {
+                cluster_id: currentCluster.id,
+              },
+              {
+                id: currentProject.id,
+                name: ingressName,
+                namespace: `${this.props.currentChart.namespace}`,
+              }
+            )
+            .then((res) => {
+              if (res.data?.spec?.rules && res.data?.spec?.rules[0]?.host) {
+                this.setState({
+                  url: `https://${res.data?.spec?.rules[0]?.host}`,
+                });
+                return;
+              }
+
+              if (res.data?.status?.loadBalancer?.ingress) {
+                this.setState({
+                  url: `http://${res.data?.status?.loadBalancer?.ingress[0]?.hostname}`,
+                });
+                return;
+              }
+            })
+            .catch(console.log);
         })
-        .catch(console.log);
-      }))
+      )
       .catch(console.log);
 
     this.updateTabs();
@@ -669,7 +677,9 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
                 <IconWrapper>{this.renderIcon()}</IconWrapper>
                 {chart.name}
               </Title>
-              {chart.chart.metadata.name != "worker" && chart.chart.metadata.name != "job" && this.renderUrl()}
+              {chart.chart.metadata.name != "worker" &&
+                chart.chart.metadata.name != "job" &&
+                this.renderUrl()}
               <InfoWrapper>
                 <StatusIndicator
                   controllers={this.state.controllers}

+ 15 - 15
dashboard/src/main/home/cluster-dashboard/expanded-chart/metrics/AreaChart.tsx

@@ -2,7 +2,7 @@ import React, { useMemo, useCallback } from "react";
 import { AreaClosed, Line, Bar } from "@visx/shape";
 import { curveMonotoneX } from "@visx/curve";
 import { scaleTime, scaleLinear } from "@visx/scale";
-import { AxisLeft, AxisBottom } from '@visx/axis';
+import { AxisLeft, AxisBottom } from "@visx/axis";
 
 import {
   withTooltip,
@@ -11,7 +11,7 @@ import {
   defaultStyles,
 } from "@visx/tooltip";
 
-import { GridRows, GridColumns } from '@visx/grid';
+import { GridRows, GridColumns } from "@visx/grid";
 
 import { WithTooltipProvidedProps } from "@visx/tooltip/lib/enhancers/withTooltip";
 import { localPoint } from "@visx/event";
@@ -24,15 +24,14 @@ export const accentColor = '#f5cb42';
 export const accentColorDark = '#949eff';
 */
 
-
 export type MetricsData = {
   date: number; // unix timestamp
-  value: number; // value 
-}
+  value: number; // value
+};
 
 type TooltipData = MetricsData;
 
-var globalData : MetricsData[]
+var globalData: MetricsData[];
 
 export const background = "#3b697800";
 export const background2 = "#20405100";
@@ -52,18 +51,19 @@ const hourFormat = timeFormat("%H:%M");
 const dayFormat = timeFormat("%b %d");
 
 // map resolutions to formats
-const formats : { [range: string]: (date: Date) => string } = {
+const formats: { [range: string]: (date: Date) => string } = {
   "1H": hourFormat,
   "6H": hourFormat,
   "1D": hourFormat,
   "1M": dayFormat,
-}
+};
 
 // accessors
-const getDate = (d: MetricsData) => new Date(d.date*1000);
+const getDate = (d: MetricsData) => new Date(d.date * 1000);
 const getValue = (d: MetricsData) => d.value;
 
-const bisectDate = bisector<MetricsData, Date>((d) => new Date(d.date*1000)).left;
+const bisectDate = bisector<MetricsData, Date>((d) => new Date(d.date * 1000))
+  .left;
 
 export type AreaProps = {
   data: MetricsData[];
@@ -86,10 +86,10 @@ export default withTooltip<AreaProps, TooltipData>(
     tooltipTop = 0,
     tooltipLeft = 0,
   }: AreaProps & WithTooltipProvidedProps<TooltipData>) => {
-    globalData = data
+    globalData = data;
 
     if (width == 0 || height == 0 || width < 10) {
-      return null
+      return null;
     }
 
     // bounds
@@ -144,7 +144,7 @@ export default withTooltip<AreaProps, TooltipData>(
         });
       },
       [showTooltip, valueScale, dateScale, width, height, data]
-    )
+    );
 
     return (
       <div>
@@ -205,7 +205,7 @@ export default withTooltip<AreaProps, TooltipData>(
             tickLabelProps={() => ({
               fill: "white",
               fontSize: 11,
-              textAnchor: 'start',
+              textAnchor: "start",
               fillOpacity: 0.4,
               dy: 0,
             })}
@@ -219,7 +219,7 @@ export default withTooltip<AreaProps, TooltipData>(
             tickLabelProps={() => ({
               fill: "white",
               fontSize: 11,
-              textAnchor: 'middle',
+              textAnchor: "middle",
               fillOpacity: 0.4,
             })}
           />

+ 149 - 126
dashboard/src/main/home/cluster-dashboard/expanded-chart/metrics/MetricsSection.tsx

@@ -28,42 +28,42 @@ type StateType = {
 };
 
 type MetricsCPUDataResponse = {
-  pod?: string,
+  pod?: string;
   results: {
-    date: number,
-    cpu: string,
-  }[],
-}[]
+    date: number;
+    cpu: string;
+  }[];
+}[];
 
 type MetricsMemoryDataResponse = {
-  pod?: string,
+  pod?: string;
   results: {
-    date: number,
-    memory: string,
-  }[],
-}[]
+    date: number;
+    memory: string;
+  }[];
+}[];
 
 type MetricsNetworkDataResponse = {
-  pod?: string,
+  pod?: string;
   results: {
-    date: number,
-    bytes: string,
-  }[],
-}[]
+    date: number;
+    bytes: string;
+  }[];
+}[];
 
-const resolutions : { [range: string]: string } = {
+const resolutions: { [range: string]: string } = {
   "1H": "15s",
   "6H": "15s",
   "1D": "15s",
   "1M": "5h",
-}
+};
 
-const secondsBeforeNow : { [range: string]: number } = {
+const secondsBeforeNow: { [range: string]: number } = {
   "1H": 60 * 60,
   "6H": 60 * 60 * 6,
   "1D": 60 * 60 * 24,
   "1M": 60 * 60 * 24 * 30,
-}
+};
 
 export default class MetricsSection extends Component<PropsType, StateType> {
   state = {
@@ -103,54 +103,60 @@ export default class MetricsSection extends Component<PropsType, StateType> {
         // TODO -- check at least one controller returned
 
         // iterate through the controllers to get the list of pods
-        this.setState({ controllers: res.data, selectedController: res.data[0] });
-        
-        this.getPods()
+        this.setState({
+          controllers: res.data,
+          selectedController: res.data[0],
+        });
+
+        this.getPods();
       })
       .catch((err) => {
         setCurrentError(JSON.stringify(err));
         this.setState({ controllers: [] });
-      });      
+      });
   }
 
   componentDidUpdate(prevProps: PropsType, prevState: StateType) {
     // if resolution, data kind, controllers, or pods have changed, update data
     if (this.state.selectedMetric != prevState.selectedMetric) {
-      this.getMetrics()
+      this.getMetrics();
     }
 
     if (this.state.selectedRange != prevState.selectedRange) {
-      this.getMetrics()
+      this.getMetrics();
     }
 
     if (this.state.selectedPod != prevState.selectedPod) {
-      this.getMetrics()
+      this.getMetrics();
     }
 
-    if (this.state.selectedController?.metadata?.name != prevState.selectedController?.metadata?.name) {
-      this.getMetrics()
+    if (
+      this.state.selectedController?.metadata?.name !=
+      prevState.selectedController?.metadata?.name
+    ) {
+      this.getMetrics();
     }
   }
 
   getMetrics = () => {
     if (this.state.pods.length == 0) {
-      return
+      return;
     }
 
     let { currentChart } = this.props;
     let { currentCluster, currentProject, setCurrentError } = this.context;
-    let kind = this.state.selectedMetric
-    let shouldsum = true
+    let kind = this.state.selectedMetric;
+    let shouldsum = true;
 
     // calculate start and end range
     var d = new Date();
     var end = Math.round(d.getTime() / 1000);
-    var start = end - secondsBeforeNow[this.state.selectedRange]
+    var start = end - secondsBeforeNow[this.state.selectedRange];
 
-    let pods = this.state.pods
+    let pods = this.state.pods;
 
     if (this.state.selectedPod != "All") {
-      pods = [this.state.selectedPod]
+      pods = [this.state.selectedPod];
     }
 
     api
@@ -173,61 +179,70 @@ export default class MetricsSection extends Component<PropsType, StateType> {
       .then((res) => {
         // transform the metrics to expected form
         if (kind == "cpu") {
-          let data = res.data as MetricsCPUDataResponse
-          
+          let data = res.data as MetricsCPUDataResponse;
+
           // if summed, just look at the first data
-            let tData = data[0].results.map(
-              (d: {
-                date: number,
-                cpu: string,
-              }, i: number) => {
-                return {
-                  date: d.date,
-                  value: parseFloat(d.cpu),
-                }
-              }
-            )
-
-            this.setState({ data: tData })
+          let tData = data[0].results.map(
+            (
+              d: {
+                date: number;
+                cpu: string;
+              },
+              i: number
+            ) => {
+              return {
+                date: d.date,
+                value: parseFloat(d.cpu),
+              };
+            }
+          );
+
+          this.setState({ data: tData });
         } else if (kind == "memory") {
-          let data = res.data as MetricsMemoryDataResponse
+          let data = res.data as MetricsMemoryDataResponse;
 
           let tData = data[0].results.map(
-            (d: {
-              date: number,
-              memory: string,
-            }, i: number) => {
+            (
+              d: {
+                date: number;
+                memory: string;
+              },
+              i: number
+            ) => {
               return {
                 date: d.date,
                 value: parseFloat(d.memory) / (1024 * 1024), // put units in Mi
-              }
+              };
             }
-          )
+          );
 
-          this.setState({ data: tData })
+          this.setState({ data: tData });
         } else if (kind == "network") {
-          let data = res.data as MetricsNetworkDataResponse
+          let data = res.data as MetricsNetworkDataResponse;
 
           let tData = data[0].results.map(
-            (d: {
-              date: number,
-              bytes: string,
-            }, i: number) => {
+            (
+              d: {
+                date: number;
+                bytes: string;
+              },
+              i: number
+            ) => {
               return {
                 date: d.date,
-                value: parseFloat(d.bytes) / (1024), // put units in Ki
-              }
+                value: parseFloat(d.bytes) / 1024, // put units in Ki
+              };
             }
-          )
+          );
 
-          this.setState({ data: tData })
+          this.setState({ data: tData });
         }
       })
       .catch((err) => {
         setCurrentError(JSON.stringify(err));
         // this.setState({ controllers: [], loading: false });
       });
-  }
+  };
 
   getPods = () => {
     let { selectedController } = this.state;
@@ -235,7 +250,8 @@ export default class MetricsSection extends Component<PropsType, StateType> {
 
     let selectors = [] as string[];
     let ml =
-      selectedController?.spec?.selector?.matchLabels || selectedController?.spec?.selector;
+      selectedController?.spec?.selector?.matchLabels ||
+      selectedController?.spec?.selector;
     let i = 1;
     let selector = "";
     for (var key in ml) {
@@ -260,19 +276,19 @@ export default class MetricsSection extends Component<PropsType, StateType> {
       )
       .then((res) => {
         let pods = res?.data?.map((pod: any) => {
-          return pod?.metadata?.name
+          return pod?.metadata?.name;
         });
 
         this.setState({ pods, selectedPod: "All" });
 
-        this.getMetrics()
+        this.getMetrics();
       })
       .catch((err) => {
         console.log(err);
         setCurrentError(JSON.stringify(err));
         return;
       });
-  }
+  };
 
   renderDropdown = () => {
     if (this.state.dropdownExpanded) {
@@ -313,7 +329,7 @@ export default class MetricsSection extends Component<PropsType, StateType> {
   };
 
   renderPodOptionList = () => {
-    let allPod = [(
+    let allPod = [
       <Option
         key={0}
         selected={"All" === this.state.selectedPod}
@@ -321,25 +337,23 @@ export default class MetricsSection extends Component<PropsType, StateType> {
         lastItem={false}
       >
         All (summed)
-      </Option>
-    )];
+      </Option>,
+    ];
 
-    let podOptions = this.state.pods.map(
-      (option: string, i: number) => {
-        return (
-          <Option
-            key={i + 1}
-            selected={option === this.state.selectedPod}
-            onClick={() => this.setState({ selectedPod: option })}
-            lastItem={i === this.state.pods.length - 1}
-          >
-            {option}
-          </Option>
-        );
-      }
-    )
+    let podOptions = this.state.pods.map((option: string, i: number) => {
+      return (
+        <Option
+          key={i + 1}
+          selected={option === this.state.selectedPod}
+          onClick={() => this.setState({ selectedPod: option })}
+          lastItem={i === this.state.pods.length - 1}
+        >
+          {option}
+        </Option>
+      );
+    });
 
-    return allPod.concat(podOptions)
+    return allPod.concat(podOptions);
   };
 
   renderControllerDropdown = () => {
@@ -362,22 +376,20 @@ export default class MetricsSection extends Component<PropsType, StateType> {
   };
 
   renderControllerOptionList = () => {
-    return this.state.controllers.map(
-      (controller: any, i: number) => {
-        let name = controller?.metadata?.name
+    return this.state.controllers.map((controller: any, i: number) => {
+      let name = controller?.metadata?.name;
 
-        return (
-          <Option
-            key={i}
-            selected={name === this.state.selectedController?.metadata?.name}
-            onClick={() => this.setState({ selectedController: controller })}
-            lastItem={i === this.state.controllers.length - 1}
-          >
-            {name}
-          </Option>
-        );
-      }
-    )
+      return (
+        <Option
+          key={i}
+          selected={name === this.state.selectedController?.metadata?.name}
+          onClick={() => this.setState({ selectedController: controller })}
+          lastItem={i === this.state.controllers.length - 1}
+        >
+          {name}
+        </Option>
+      );
+    });
   };
 
   renderOptionList = () => {
@@ -392,7 +404,12 @@ export default class MetricsSection extends Component<PropsType, StateType> {
           <Option
             key={i}
             selected={option.value === this.state.selectedMetric}
-            onClick={() => this.setState({ selectedMetric: option.value, selectedMetricLabel: option.label })}
+            onClick={() =>
+              this.setState({
+                selectedMetric: option.value,
+                selectedMetricLabel: option.label,
+              })
+            }
             lastItem={i === metricOptions.length - 1}
           >
             {option.label}
@@ -406,37 +423,44 @@ export default class MetricsSection extends Component<PropsType, StateType> {
     return (
       <StyledMetricsSection>
         <ParentSize>
-          {({ width, height }) => <AreaChart 
-            data={this.state.data} 
-            width={width} 
-            height={height} 
-            resolution={this.state.selectedRange}
-            margin={{ top: 60, right: -40, bottom: 0, left: 50 }}
-          />}
+          {({ width, height }) => (
+            <AreaChart
+              data={this.state.data}
+              width={width}
+              height={height}
+              resolution={this.state.selectedRange}
+              margin={{ top: 60, right: -40, bottom: 0, left: 50 }}
+            />
+          )}
         </ParentSize>
         <MetricSelector
           onClick={() =>
             this.setState({ dropdownExpanded: !this.state.dropdownExpanded })
           }
         >
-          <MetricsLabel>
-          {this.state.selectedMetricLabel}
-          </MetricsLabel>
+          <MetricsLabel>{this.state.selectedMetricLabel}</MetricsLabel>
           <i className="material-icons">arrow_drop_down</i>
           {this.renderDropdown()}
         </MetricSelector>
         <ControllerSelector
           onClick={() =>
-            this.setState({ controllerDropdownExpanded: !this.state.controllerDropdownExpanded })
+            this.setState({
+              controllerDropdownExpanded: !this.state
+                .controllerDropdownExpanded,
+            })
           }
         >
-          <MetricsLabel>{this.state.selectedController?.metadata?.name}</MetricsLabel>
+          <MetricsLabel>
+            {this.state.selectedController?.metadata?.name}
+          </MetricsLabel>
           <i className="material-icons">arrow_drop_down</i>
           {this.renderControllerDropdown()}
         </ControllerSelector>
         <PodSelector
           onClick={() =>
-            this.setState({ podDropdownExpanded: !this.state.podDropdownExpanded })
+            this.setState({
+              podDropdownExpanded: !this.state.podDropdownExpanded,
+            })
           }
         >
           <MetricsLabel>{this.state.selectedPod}</MetricsLabel>
@@ -512,7 +536,6 @@ const Dropdown = styled.div`
   box-shadow: 0 4px 8px 0px #00000088;
 `;
 
-
 const RangeWrapper = styled.div`
   position: absolute;
   top: 0;
@@ -546,19 +569,19 @@ const MetricSelector = styled.div`
 `;
 
 const MetricsLabel = styled.div`
-white-space: nowrap;
-text-overflow: ellipsis;
-overflow: hidden;
-max-width: 200px;
-`
+  white-space: nowrap;
+  text-overflow: ellipsis;
+  overflow: hidden;
+  max-width: 200px;
+`;
 
 const ControllerSelector = styled(MetricSelector)`
   left: 230px;
-`
+`;
 
 const PodSelector = styled(MetricSelector)`
   left: 490px;
-`
+`;
 
 const StyledMetricsSection = styled.div`
   width: 100%;

+ 10 - 7
dashboard/src/main/home/cluster-dashboard/expanded-chart/status/ControllerTab.tsx

@@ -50,7 +50,7 @@ export default class ControllerTab extends Component<PropsType, StateType> {
     if (selectors.length == 0 && this.props.selectors) {
       selectors = this.props.selectors;
     }
-    
+
     api
       .getMatchingPods(
         "<token>",
@@ -111,7 +111,7 @@ export default class ControllerTab extends Component<PropsType, StateType> {
           c.status?.desiredNumberScheduled || 0,
         ];
       case "job":
-        return [1, 1]
+        return [1, 1];
     }
   };
 
@@ -156,10 +156,10 @@ export default class ControllerTab extends Component<PropsType, StateType> {
     let status = available == total ? "running" : "waiting";
 
     if (controller.kind?.toLowerCase() == "job" && !controller.status.active) {
-      status = "completed"
+      status = "completed";
     }
 
-    console.log("STATUS", status)
+    console.log("STATUS", status);
 
     return (
       <ResourceTab
@@ -171,8 +171,11 @@ export default class ControllerTab extends Component<PropsType, StateType> {
       >
         {this.state.raw.map((pod, i) => {
           let status = this.getPodStatus(pod.status);
-          if (controller.kind?.toLowerCase() == "job" && !controller.status.active) {
-            status = "completed"
+          if (
+            controller.kind?.toLowerCase() == "job" &&
+            !controller.status.active
+          ) {
+            status = "completed";
           }
           return (
             <Tab
@@ -278,7 +281,7 @@ const StatusColor = styled.div`
       : props.status === "failed"
       ? "#ed5f85"
       : props.status === "completed"
-      ? '#00d12a'
+      ? "#00d12a"
       : "#f5cb42"};
   border-radius: 20px;
 `;

+ 1 - 1
dashboard/src/main/home/cluster-dashboard/expanded-chart/status/Logs.tsx

@@ -46,7 +46,7 @@ export default class Logs extends Component<PropsType, StateType> {
         <Message>
           ⌛ This job has been completed. You can now delete this job.
         </Message>
-      )
+      );
     }
 
     if (this.state.logs.length == 0) {

+ 20 - 12
dashboard/src/main/home/launch/Launch.tsx

@@ -52,13 +52,19 @@ export default class Templates extends Component<PropsType, StateType> {
       .catch(() => this.setState({ loading: false, error: true }));
 
     api
-      .getApplicationTemplates("<token>", { 
-        repo_url: process.env.APPLICATION_CHART_REPO_URL 
-      }, {})
+      .getApplicationTemplates(
+        "<token>",
+        {
+          repo_url: process.env.APPLICATION_CHART_REPO_URL,
+        },
+        {}
+      )
       .then((res) => {
-        console.log(res.data)
-        this.setState({ applicationTemplates: res.data, error: false }, () => {    
-          this.state.applicationTemplates.sort((a, b) => (a.version > b.version ? 1 : -1));      
+        console.log(res.data);
+        this.setState({ applicationTemplates: res.data, error: false }, () => {
+          this.state.applicationTemplates.sort((a, b) =>
+            a.version > b.version ? 1 : -1
+          );
           this.setState({
             loading: false,
           });
@@ -102,8 +108,8 @@ export default class Templates extends Component<PropsType, StateType> {
       );
     }
 
-    return this.state.applicationTemplates
-      .map((template: PorterTemplate, i: number) => {
+    return this.state.applicationTemplates.map(
+      (template: PorterTemplate, i: number) => {
         let { name, icon, description } = template;
         if (hardcodedNames[name]) {
           name = hardcodedNames[name];
@@ -118,7 +124,8 @@ export default class Templates extends Component<PropsType, StateType> {
             <TemplateDescription>{description}</TemplateDescription>
           </TemplateBlock>
         );
-      });
+      }
+    );
   };
 
   renderAddonList = () => {
@@ -144,8 +151,8 @@ export default class Templates extends Component<PropsType, StateType> {
       );
     }
 
-    return this.state.addonTemplates
-      .map((template: PorterTemplate, i: number) => {
+    return this.state.addonTemplates.map(
+      (template: PorterTemplate, i: number) => {
         let { name, icon, description } = template;
         if (hardcodedNames[name]) {
           name = hardcodedNames[name];
@@ -160,7 +167,8 @@ export default class Templates extends Component<PropsType, StateType> {
             <TemplateDescription>{description}</TemplateDescription>
           </TemplateBlock>
         );
-      });
+      }
+    );
   };
 
   renderApplicationTemplates = () => {

+ 9 - 10
dashboard/src/main/home/launch/expanded-template/ExpandedTemplate.tsx

@@ -42,17 +42,16 @@ export default class ExpandedTemplate extends Component<PropsType, StateType> {
 
   fetchTemplateInfo = () => {
     this.setState({ loading: true });
-    let params = this.props.currentTab == "docker" ? { repo_url: process.env.APPLICATION_CHART_REPO_URL} : {}
-    
+    let params =
+      this.props.currentTab == "docker"
+        ? { repo_url: process.env.APPLICATION_CHART_REPO_URL }
+        : {};
+
     api
-      .getTemplateInfo(
-        "<token>",
-        params,
-        {
-          name: this.props.currentTemplate.name.toLowerCase().trim(),
-          version: "latest",
-        }
-      )
+      .getTemplateInfo("<token>", params, {
+        name: this.props.currentTemplate.name.toLowerCase().trim(),
+        version: "latest",
+      })
       .then((res) => {
         let { form, values, markdown, metadata } = res.data;
         let keywords = metadata.keywords;

+ 7 - 3
dashboard/src/main/home/launch/expanded-template/LaunchTemplate.tsx

@@ -644,9 +644,7 @@ class LaunchTemplate extends Component<PropsType, StateType> {
           <i className="material-icons" onClick={this.props.hideLaunch}>
             keyboard_backspace
           </i>
-          {icon
-            ? this.renderIcon(icon)
-            : this.renderIcon(currentTemplate.icon)}
+          {icon ? this.renderIcon(icon) : this.renderIcon(currentTemplate.icon)}
           <Title>{name}</Title>
         </HeaderSection>
         <DarkMatter antiHeight="-13px" />
@@ -719,6 +717,12 @@ class LaunchTemplate extends Component<PropsType, StateType> {
 LaunchTemplate.contextType = Context;
 export default withRouter(LaunchTemplate);
 
+const Bold = styled.div`
+  font-weight: bold;
+  color: white;
+  margin-right: 5px;
+`;
+
 const CloseButton = styled.div`
   position: absolute;
   display: block;

+ 1 - 1
dashboard/src/main/home/launch/expanded-template/TemplateInfo.tsx

@@ -74,7 +74,7 @@ export default class TemplateInfo extends Component<PropsType, StateType> {
           </Banner>
         </>
       );
-    } else if (this.props.currentTab == 'docker') {
+    } else if (this.props.currentTab == "docker") {
       return (
         <>
           <Br />

+ 1 - 1
dashboard/src/main/home/launch/hardcodedNameDict.tsx

@@ -9,7 +9,7 @@ const hardcodedNames: { [key: string]: string } = {
   ubuntu: "Ubuntu",
   web: "Web Service",
   worker: "Worker",
-  job: "Job"
+  job: "Job",
 };
 
 export default hardcodedNames;

+ 2 - 2
dashboard/src/shared/Context.tsx

@@ -72,9 +72,9 @@ class ContextProvider extends Component {
         currentProject: null,
         projects: [],
         user: null,
-        devOpsMode: true
+        devOpsMode: true,
       });
-    }
+    },
   };
 
   render() {

+ 3 - 3
dashboard/src/shared/ansiparser.tsx

@@ -8,7 +8,7 @@ const foregroundColors = {
   "35": "magenta",
   "36": "cyan",
   "37": "white",
-  "90": "grey"
+  "90": "grey",
 } as Record<string, string>;
 
 const backgroundColors = {
@@ -19,13 +19,13 @@ const backgroundColors = {
   "44": "blue",
   "45": "magenta",
   "46": "cyan",
-  "47": "white"
+  "47": "white",
 } as Record<string, string>;
 
 const styles = {
   "1": "bold",
   "3": "italic",
-  "4": "underline"
+  "4": "underline",
 } as Record<string, string>;
 
 const eraseChar = (matchingText: any, result: any) => {

+ 68 - 65
dashboard/src/shared/api.tsx

@@ -18,7 +18,7 @@ const connectECRRegistry = baseApi<
     aws_integration_id: string;
   },
   { id: number }
->("POST", pathParams => {
+>("POST", (pathParams) => {
   return `/api/projects/${pathParams.id}/registries`;
 });
 
@@ -29,7 +29,7 @@ const connectGCRRegistry = baseApi<
     url: string;
   },
   { id: number }
->("POST", pathParams => {
+>("POST", (pathParams) => {
   return `/api/projects/${pathParams.id}/registries`;
 });
 
@@ -41,7 +41,7 @@ const createAWSIntegration = baseApi<
     aws_secret_access_key: string;
   },
   { id: number }
->("POST", pathParams => {
+>("POST", (pathParams) => {
   return `/api/projects/${pathParams.id}/integrations/aws`;
 });
 
@@ -54,7 +54,7 @@ const createDOCR = baseApi<
   {
     project_id: number;
   }
->("POST", pathParams => {
+>("POST", (pathParams) => {
   return `/api/projects/${pathParams.project_id}/provision/docr`;
 });
 
@@ -67,7 +67,7 @@ const createDOKS = baseApi<
   {
     project_id: number;
   }
->("POST", pathParams => {
+>("POST", (pathParams) => {
   return `/api/projects/${pathParams.project_id}/provision/doks`;
 });
 
@@ -80,7 +80,7 @@ const createGCPIntegration = baseApi<
   {
     project_id: number;
   }
->("POST", pathParams => {
+>("POST", (pathParams) => {
   return `/api/projects/${pathParams.project_id}/integrations/gcp`;
 });
 
@@ -91,7 +91,7 @@ const createGCR = baseApi<
   {
     project_id: number;
   }
->("POST", pathParams => {
+>("POST", (pathParams) => {
   return `/api/projects/${pathParams.project_id}/provision/gcr`;
 });
 
@@ -111,7 +111,7 @@ const createGHAction = baseApi<
     RELEASE_NAME: string;
     RELEASE_NAMESPACE: string;
   }
->("POST", pathParams => {
+>("POST", (pathParams) => {
   let { project_id, CLUSTER_ID, RELEASE_NAME, RELEASE_NAMESPACE } = pathParams;
   return `/api/projects/${project_id}/ci/actions?cluster_id=${CLUSTER_ID}&name=${RELEASE_NAME}&namespace=${RELEASE_NAMESPACE}`;
 });
@@ -124,7 +124,7 @@ const createGKE = baseApi<
   {
     project_id: number;
   }
->("POST", pathParams => {
+>("POST", (pathParams) => {
   return `/api/projects/${pathParams.project_id}/provision/gke`;
 });
 
@@ -135,11 +135,11 @@ const createInvite = baseApi<
   {
     id: number;
   }
->("POST", pathParams => {
+>("POST", (pathParams) => {
   return `/api/projects/${pathParams.id}/invites`;
 });
 
-const createProject = baseApi<{ name: string }, {}>("POST", pathParams => {
+const createProject = baseApi<{ name: string }, {}>("POST", (pathParams) => {
   return `/api/projects`;
 });
 
@@ -149,18 +149,18 @@ const deleteCluster = baseApi<
     project_id: number;
     cluster_id: number;
   }
->("DELETE", pathParams => {
+>("DELETE", (pathParams) => {
   return `/api/projects/${pathParams.project_id}/clusters/${pathParams.cluster_id}`;
 });
 
 const deleteInvite = baseApi<{}, { id: number; invId: number }>(
   "DELETE",
-  pathParams => {
+  (pathParams) => {
     return `/api/projects/${pathParams.id}/invites/${pathParams.invId}`;
   }
 );
 
-const deleteProject = baseApi<{}, { id: number }>("DELETE", pathParams => {
+const deleteProject = baseApi<{}, { id: number }>("DELETE", (pathParams) => {
   return `/api/projects/${pathParams.id}`;
 });
 
@@ -180,7 +180,7 @@ const deployTemplate = baseApi<
     version: string;
     repo_url?: string;
   }
->("POST", pathParams => {
+>("POST", (pathParams) => {
   let { cluster_id, id, name, version, repo_url } = pathParams;
 
   if (repo_url) {
@@ -197,7 +197,7 @@ const destroyCluster = baseApi<
     project_id: number;
     infra_id: number;
   }
->("POST", pathParams => {
+>("POST", (pathParams) => {
   return `/api/projects/${pathParams.project_id}/infra/${pathParams.infra_id}/eks/destroy`;
 });
 
@@ -213,7 +213,7 @@ const getBranchContents = baseApi<
     name: string;
     branch: string;
   }
->("GET", pathParams => {
+>("GET", (pathParams) => {
   return `/api/projects/${pathParams.project_id}/gitrepos/${pathParams.git_repo_id}/repos/${pathParams.kind}/${pathParams.owner}/${pathParams.name}/${pathParams.branch}/contents`;
 });
 
@@ -226,7 +226,7 @@ const getBranches = baseApi<
     owner: string;
     name: string;
   }
->("GET", pathParams => {
+>("GET", (pathParams) => {
   return `/api/projects/${pathParams.project_id}/gitrepos/${pathParams.git_repo_id}/repos/${pathParams.kind}/${pathParams.owner}/${pathParams.name}/branches`;
 });
 
@@ -237,7 +237,7 @@ const getChart = baseApi<
     storage: StorageType;
   },
   { id: number; name: string; revision: number }
->("GET", pathParams => {
+>("GET", (pathParams) => {
   return `/api/projects/${pathParams.id}/releases/${pathParams.name}/${pathParams.revision}`;
 });
 
@@ -252,7 +252,7 @@ const getCharts = baseApi<
     statusFilter: string[];
   },
   { id: number }
->("GET", pathParams => {
+>("GET", (pathParams) => {
   return `/api/projects/${pathParams.id}/releases`;
 });
 
@@ -263,7 +263,7 @@ const getChartComponents = baseApi<
     storage: StorageType;
   },
   { id: number; name: string; revision: number }
->("GET", pathParams => {
+>("GET", (pathParams) => {
   return `/api/projects/${pathParams.id}/releases/${pathParams.name}/${pathParams.revision}/components`;
 });
 
@@ -274,13 +274,13 @@ const getChartControllers = baseApi<
     storage: StorageType;
   },
   { id: number; name: string; revision: number }
->("GET", pathParams => {
+>("GET", (pathParams) => {
   return `/api/projects/${pathParams.id}/releases/${pathParams.name}/${pathParams.revision}/controllers`;
 });
 
 const getClusterIntegrations = baseApi("GET", "/api/integrations/cluster");
 
-const getClusters = baseApi<{}, { id: number }>("GET", pathParams => {
+const getClusters = baseApi<{}, { id: number }>("GET", (pathParams) => {
   return `/api/projects/${pathParams.id}/clusters`;
 });
 
@@ -290,7 +290,7 @@ const getGitRepoList = baseApi<
     project_id: number;
     git_repo_id: number;
   }
->("GET", pathParams => {
+>("GET", (pathParams) => {
   return `/api/projects/${pathParams.project_id}/gitrepos/${pathParams.git_repo_id}/repos`;
 });
 
@@ -299,7 +299,7 @@ const getGitRepos = baseApi<
   {
     project_id: number;
   }
->("GET", pathParams => {
+>("GET", (pathParams) => {
   return `/api/projects/${pathParams.project_id}/gitrepos`;
 });
 
@@ -309,7 +309,7 @@ const getImageRepos = baseApi<
     project_id: number;
     registry_id: number;
   }
->("GET", pathParams => {
+>("GET", (pathParams) => {
   return `/api/projects/${pathParams.project_id}/registries/${pathParams.registry_id}/repositories`;
 });
 
@@ -320,7 +320,7 @@ const getImageTags = baseApi<
     registry_id: number;
     repo_name: string;
   }
->("GET", pathParams => {
+>("GET", (pathParams) => {
   return `/api/projects/${pathParams.project_id}/registries/${pathParams.registry_id}/repositories/${pathParams.repo_name}`;
 });
 
@@ -329,7 +329,7 @@ const getInfra = baseApi<
   {
     project_id: number;
   }
->("GET", pathParams => {
+>("GET", (pathParams) => {
   return `/api/projects/${pathParams.project_id}/infra`;
 });
 
@@ -338,11 +338,11 @@ const getIngress = baseApi<
     cluster_id: number;
   },
   { name: string; namespace: string; id: number }
->("GET", pathParams => {
+>("GET", (pathParams) => {
   return `/api/projects/${pathParams.id}/k8s/${pathParams.namespace}/ingress/${pathParams.name}`;
 });
 
-const getInvites = baseApi<{}, { id: number }>("GET", pathParams => {
+const getInvites = baseApi<{}, { id: number }>("GET", (pathParams) => {
   return `/api/projects/${pathParams.id}/invites`;
 });
 
@@ -352,7 +352,7 @@ const getMatchingPods = baseApi<
     selectors: string[];
   },
   { id: number }
->("GET", pathParams => {
+>("GET", (pathParams) => {
   return `/api/projects/${pathParams.id}/k8s/pods`;
 });
 
@@ -368,7 +368,7 @@ const getMetrics = baseApi<
     resolution: string;
   },
   { id: number }
->("GET", pathParams => {
+>("GET", (pathParams) => {
   return `/api/projects/${pathParams.id}/k8s/metrics`;
 });
 
@@ -377,7 +377,7 @@ const getNamespaces = baseApi<
     cluster_id: number;
   },
   { id: number }
->("GET", pathParams => {
+>("GET", (pathParams) => {
   return `/api/projects/${pathParams.id}/k8s/namespaces`;
 });
 
@@ -386,23 +386,26 @@ const getOAuthIds = baseApi<
   {
     project_id: number;
   }
->("GET", pathParams => {
+>("GET", (pathParams) => {
   return `/api/projects/${pathParams.project_id}/integrations/oauth`;
 });
 
-const getProjectClusters = baseApi<{}, { id: number }>("GET", pathParams => {
+const getProjectClusters = baseApi<{}, { id: number }>("GET", (pathParams) => {
   return `/api/projects/${pathParams.id}/clusters`;
 });
 
-const getProjectRegistries = baseApi<{}, { id: number }>("GET", pathParams => {
-  return `/api/projects/${pathParams.id}/registries`;
-});
+const getProjectRegistries = baseApi<{}, { id: number }>(
+  "GET",
+  (pathParams) => {
+    return `/api/projects/${pathParams.id}/registries`;
+  }
+);
 
-const getProjectRepos = baseApi<{}, { id: number }>("GET", pathParams => {
+const getProjectRepos = baseApi<{}, { id: number }>("GET", (pathParams) => {
   return `/api/projects/${pathParams.id}/repos`;
 });
 
-const getProjects = baseApi<{}, { id: number }>("GET", pathParams => {
+const getProjects = baseApi<{}, { id: number }>("GET", (pathParams) => {
   return `/api/users/${pathParams.id}/projects`;
 });
 
@@ -411,7 +414,7 @@ const getPrometheusIsInstalled = baseApi<
     cluster_id: number;
   },
   { id: number }
->("GET", pathParams => {
+>("GET", (pathParams) => {
   return `/api/projects/${pathParams.id}/k8s/prometheus/detect`;
 });
 
@@ -424,7 +427,7 @@ const getReleaseToken = baseApi<
     storage: StorageType;
   },
   { name: string; id: number }
->("GET", pathParams => {
+>("GET", (pathParams) => {
   return `/api/projects/${pathParams.id}/releases/${pathParams.name}/webhook_token`;
 });
 
@@ -436,7 +439,7 @@ const destroyEKS = baseApi<
     project_id: number;
     infra_id: number;
   }
->("POST", pathParams => {
+>("POST", (pathParams) => {
   return `/api/projects/${pathParams.project_id}/infra/${pathParams.infra_id}/eks/destroy`;
 });
 
@@ -448,7 +451,7 @@ const destroyGKE = baseApi<
     project_id: number;
     infra_id: number;
   }
->("POST", pathParams => {
+>("POST", (pathParams) => {
   return `/api/projects/${pathParams.project_id}/infra/${pathParams.infra_id}/gke/destroy`;
 });
 
@@ -460,13 +463,13 @@ const destroyDOKS = baseApi<
     project_id: number;
     infra_id: number;
   }
->("POST", pathParams => {
+>("POST", (pathParams) => {
   return `/api/projects/${pathParams.project_id}/infra/${pathParams.infra_id}/doks/destroy`;
 });
 
 const getRepoIntegrations = baseApi("GET", "/api/integrations/repo");
 
-const getRepos = baseApi<{}, { id: number }>("GET", pathParams => {
+const getRepos = baseApi<{}, { id: number }>("GET", (pathParams) => {
   return `/api/projects/${pathParams.id}/repos`;
 });
 
@@ -477,29 +480,29 @@ const getRevisions = baseApi<
     storage: StorageType;
   },
   { id: number; name: string }
->("GET", pathParams => {
+>("GET", (pathParams) => {
   return `/api/projects/${pathParams.id}/releases/${pathParams.name}/history`;
 });
 
-const getTemplateInfo = baseApi<{
-  repo_url?: string
-}, { name: string; version: string }>(
-  "GET",
-  pathParams => {
-    return `/api/templates/${pathParams.name}/${pathParams.version}`;
-  }
-);
+const getTemplateInfo = baseApi<
+  {
+    repo_url?: string;
+  },
+  { name: string; version: string }
+>("GET", (pathParams) => {
+  return `/api/templates/${pathParams.name}/${pathParams.version}`;
+});
 
 const getAddonTemplates = baseApi("GET", "/api/templates");
 
 const getApplicationTemplates = baseApi<
   {
-    repo_url?: string
+    repo_url?: string;
   },
   {}
 >("GET", "/api/templates");
 
-const getUser = baseApi<{}, { id: number }>("GET", pathParams => {
+const getUser = baseApi<{}, { id: number }>("GET", (pathParams) => {
   return `/api/users/${pathParams.id}`;
 });
 
@@ -508,7 +511,7 @@ const linkGithubProject = baseApi<
   {
     project_id: number;
   }
->("GET", pathParams => {
+>("GET", (pathParams) => {
   return `/api/oauth/projects/${pathParams.project_id}/github`;
 });
 
@@ -525,7 +528,7 @@ const provisionECR = baseApi<
     aws_integration_id: string;
   },
   { id: number }
->("POST", pathParams => {
+>("POST", (pathParams) => {
   return `/api/projects/${pathParams.id}/provision/ecr`;
 });
 
@@ -535,7 +538,7 @@ const provisionEKS = baseApi<
     aws_integration_id: string;
   },
   { id: number }
->("POST", pathParams => {
+>("POST", (pathParams) => {
   return `/api/projects/${pathParams.id}/provision/eks`;
 });
 
@@ -555,7 +558,7 @@ const rollbackChart = baseApi<
     name: string;
     cluster_id: number;
   }
->("POST", pathParams => {
+>("POST", (pathParams) => {
   let { id, name, cluster_id } = pathParams;
   return `/api/projects/${id}/releases/${name}/rollback?cluster_id=${cluster_id}`;
 });
@@ -569,7 +572,7 @@ const uninstallTemplate = baseApi<
     namespace: string;
     storage: StorageType;
   }
->("POST", pathParams => {
+>("POST", (pathParams) => {
   let { id, name, cluster_id, storage, namespace } = pathParams;
   return `/api/projects/${id}/delete/${name}?cluster_id=${cluster_id}&namespace=${namespace}&storage=${storage}`;
 });
@@ -580,7 +583,7 @@ const updateUser = baseApi<
     allowedContexts?: string[];
   },
   { id: number }
->("PUT", pathParams => {
+>("PUT", (pathParams) => {
   return `/api/users/${pathParams.id}`;
 });
 
@@ -595,7 +598,7 @@ const upgradeChartValues = baseApi<
     name: string;
     cluster_id: number;
   }
->("POST", pathParams => {
+>("POST", (pathParams) => {
   let { id, name, cluster_id } = pathParams;
   return `/api/projects/${id}/releases/${name}/upgrade?cluster_id=${cluster_id}`;
 });
@@ -663,5 +666,5 @@ export default {
   rollbackChart,
   uninstallTemplate,
   updateUser,
-  upgradeChartValues
+  upgradeChartValues,
 };

+ 6 - 6
dashboard/src/shared/baseApi.tsx

@@ -21,23 +21,23 @@ export const baseApi = <T extends {}, S = {}>(
     if (requestType === "POST") {
       return axios.post(endpointString, params, {
         headers: {
-          Authorization: `Bearer ${token}`
-        }
+          Authorization: `Bearer ${token}`,
+        },
       });
     } else if (requestType === "PUT") {
       return axios.put(endpointString, params, {
         headers: {
-          Authorization: `Bearer ${token}`
-        }
+          Authorization: `Bearer ${token}`,
+        },
       });
     } else if (requestType === "DELETE") {
       return axios.delete(endpointString, params);
     } else {
       return axios.get(endpointString, {
         params,
-        paramsSerializer: function(params) {
+        paramsSerializer: function (params) {
           return qs.stringify(params, { arrayFormat: "repeat" });
-        }
+        },
       });
     }
   };

+ 17 - 17
dashboard/src/shared/common.tsx

@@ -10,7 +10,7 @@ export const infraNames: any = {
   gcr: "Google Container Registry (GCR)",
   gke: "Google Kubernetes Engine (GKE)",
   docr: "Digital Ocean Container Registry",
-  doks: "Digital Ocean Kubernetes Service"
+  doks: "Digital Ocean Kubernetes Service",
 };
 
 export const integrationList: any = {
@@ -18,68 +18,68 @@ export const integrationList: any = {
     icon:
       "https://uxwing.com/wp-content/themes/uxwing/download/10-brands-and-social-media/kubernetes.png",
     label: "Kubernetes Cluster",
-    buttonText: "Add a Cluster"
+    buttonText: "Add a Cluster",
   },
   repo: {
     icon:
       "https://3.bp.blogspot.com/-xhNpNJJyQhk/XIe4GY78RQI/AAAAAAAAItc/ouueFUj2Hqo5dntmnKqEaBJR4KQ4Q2K3ACK4BGAYYCw/s1600/logo%2Bgit%2Bicon.png",
     label: "Git Repository",
-    buttonText: "Link a Github Account"
+    buttonText: "Link a Github Account",
   },
   registry: {
     icon:
       "https://cdn4.iconfinder.com/data/icons/logos-and-brands/512/97_Docker_logo_logos-512.png",
     label: "Docker Registry",
-    buttonText: "Add a Registry"
+    buttonText: "Add a Registry",
   },
   gke: {
     icon: "https://sysdig.com/wp-content/uploads/2016/08/GKE_color.png",
-    label: "Google Kubernetes Engine (GKE)"
+    label: "Google Kubernetes Engine (GKE)",
   },
   eks: {
     icon: "https://img.stackshare.io/service/7991/amazon-eks.png",
-    label: "Amazon Elastic Kubernetes Service (EKS)"
+    label: "Amazon Elastic Kubernetes Service (EKS)",
   },
   kube: {
     icon:
       "https://uxwing.com/wp-content/themes/uxwing/download/10-brands-and-social-media/kubernetes.png",
-    label: "Upload Kubeconfig"
+    label: "Upload Kubeconfig",
   },
   docker: {
     icon:
       "https://cdn4.iconfinder.com/data/icons/logos-and-brands/512/97_Docker_logo_logos-512.png",
-    label: "Docker Hub"
+    label: "Docker Hub",
   },
   gcr: {
     icon:
       "https://carlossanchez.files.wordpress.com/2019/06/21046548.png?w=640",
-    label: "Google Container Registry (GCR)"
+    label: "Google Container Registry (GCR)",
   },
   ecr: {
     icon:
       "https://avatars2.githubusercontent.com/u/52505464?s=400&u=da920f994c67665c7ad6c606a5286557d4f8555f&v=4",
-    label: "Elastic Container Registry (ECR)"
+    label: "Elastic Container Registry (ECR)",
   },
   aws: {
     icon: aws,
-    label: "AWS"
+    label: "AWS",
   },
   gcp: {
     icon: gcp,
-    label: "GCP"
+    label: "GCP",
   },
   do: {
     icon: digitalOcean,
-    label: "DigitalOcean"
+    label: "DigitalOcean",
   },
   github: {
     icon: github,
-    label: "GitHub"
+    label: "GitHub",
   },
   gitlab: {
     icon: "https://about.gitlab.com/images/press/logo/png/gitlab-icon-rgb.png",
-    label: "Gitlab"
-  }
+    label: "Gitlab",
+  },
 };
 
 export const isAlphanumeric = (x: string | null) => {
@@ -92,6 +92,6 @@ export const isAlphanumeric = (x: string | null) => {
 
 export const getIgnoreCase = (object: any, key: string) => {
   return object[
-    Object.keys(object).find(k => k.toLowerCase() === key.toLowerCase())
+    Object.keys(object).find((k) => k.toLowerCase() === key.toLowerCase())
   ];
 };

+ 5 - 5
dashboard/src/shared/feedback.tsx

@@ -10,18 +10,18 @@ export const handleSubmitFeedback = (
       {
         key: process.env.DISCORD_KEY,
         cid: process.env.DISCORD_CID,
-        message: msg
+        message: msg,
       },
       {
         headers: {
-          Authorization: `Bearer <>`
-        }
+          Authorization: `Bearer <>`,
+        },
       }
     )
-    .then(res => {
+    .then((res) => {
       callback && callback(null, res);
     })
-    .catch(err => {
+    .catch((err) => {
       callback && callback(err, null);
     });
 };

+ 2 - 2
dashboard/src/shared/rosettaStone.tsx

@@ -11,11 +11,11 @@ export const kindToIcon: { [kind: string]: string } = {
   Role: "portrait",
   RoleBinding: "swap_horizontal_circle",
   ConfigMap: "map",
-  PodSecurityPolicy: "security"
+  PodSecurityPolicy: "security",
 };
 
 export const edgeColors: { [kind: string]: string } = {
   LabelRel: "#32a85f",
   ControlRel: "#fcb603",
-  SpecRel: "#949EFF"
+  SpecRel: "#949EFF",
 };

+ 2 - 2
dashboard/src/shared/routing.tsx

@@ -14,7 +14,7 @@ export const PorterUrls = [
   "integrations",
   "new-project",
   "cluster-dashboard",
-  "project-settings"
+  "project-settings",
 ];
 
 export const setSearchParam = (
@@ -26,6 +26,6 @@ export const setSearchParam = (
   urlParams.set(key, value);
   return {
     pathname: location.pathname,
-    search: urlParams.toString()
+    search: urlParams.toString(),
   };
 };

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

@@ -66,7 +66,7 @@ export interface EdgeType {
 export enum StorageType {
   Secret = "secret",
   ConfigMap = "configmap",
-  Memory = "memory"
+  Memory = "memory",
 }
 
 // PorterTemplate represents a bundled Porter template