Просмотр исходного кода

Merge pull request #2427 from porter-dev/fe-pass

ingress IP -> cluster settings, flattened list views, clearer back buttons
Porter Support 3 лет назад
Родитель
Сommit
9f82f4383c
40 измененных файлов с 592 добавлено и 632 удалено
  1. 4 0
      dashboard/src/assets/left-arrow.svg
  2. 4 3
      dashboard/src/components/TitleSection.tsx
  3. 53 41
      dashboard/src/components/expanded-object/Header.tsx
  4. 2 2
      dashboard/src/main/home/Home.tsx
  5. 2 2
      dashboard/src/main/home/cluster-dashboard/ClusterDashboard.tsx
  6. 5 5
      dashboard/src/main/home/cluster-dashboard/DashboardHeader.tsx
  7. 9 62
      dashboard/src/main/home/cluster-dashboard/chart/Chart.tsx
  8. 105 7
      dashboard/src/main/home/cluster-dashboard/dashboard/Dashboard.tsx
  9. 1 1
      dashboard/src/main/home/cluster-dashboard/dashboard/Metrics.tsx
  10. 39 6
      dashboard/src/main/home/cluster-dashboard/dashboard/node-view/ExpandedNodeView.tsx
  11. 1 1
      dashboard/src/main/home/cluster-dashboard/env-groups/CreateEnvGroup.tsx
  12. 10 59
      dashboard/src/main/home/cluster-dashboard/env-groups/EnvGroup.tsx
  13. 1 1
      dashboard/src/main/home/cluster-dashboard/env-groups/EnvGroupDashboard.tsx
  14. 39 3
      dashboard/src/main/home/cluster-dashboard/env-groups/ExpandedEnvGroup.tsx
  15. 34 109
      dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedChart.tsx
  16. 1 2
      dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedChartWrapper.tsx
  17. 84 47
      dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedJobChart.tsx
  18. 39 4
      dashboard/src/main/home/cluster-dashboard/expanded-chart/jobs/ExpandedJobRun.tsx
  19. 4 12
      dashboard/src/main/home/cluster-dashboard/preview-environments/environments/EnvironmentCard.tsx
  20. 1 1
      dashboard/src/main/home/cluster-dashboard/stacks/Dashboard.tsx
  21. 40 5
      dashboard/src/main/home/cluster-dashboard/stacks/ExpandedStack/ExpandedStack.tsx
  22. 1 1
      dashboard/src/main/home/cluster-dashboard/stacks/_StackList.tsx
  23. 2 2
      dashboard/src/main/home/cluster-dashboard/stacks/launch/Overview.tsx
  24. 4 4
      dashboard/src/main/home/cluster-dashboard/stacks/launch/components/styles.tsx
  25. 72 154
      dashboard/src/main/home/dashboard/ClusterList.tsx
  26. 11 12
      dashboard/src/main/home/dashboard/Dashboard.tsx
  27. 1 6
      dashboard/src/main/home/integrations/Integrations.tsx
  28. 3 3
      dashboard/src/main/home/launch/Launch.tsx
  29. 2 3
      dashboard/src/main/home/launch/launch-flow/LaunchFlow.tsx
  30. 2 62
      dashboard/src/main/home/navbar/Navbar.tsx
  31. 2 2
      dashboard/src/main/home/new-project/NewProject.tsx
  32. 2 2
      dashboard/src/main/home/onboarding/steps/ConnectRegistry/ConnectRegistry.tsx
  33. 1 1
      dashboard/src/main/home/onboarding/steps/ConnectSource.tsx
  34. 1 1
      dashboard/src/main/home/onboarding/steps/ProvisionResources/ProvisionResources.tsx
  35. 1 1
      dashboard/src/main/home/project-settings/ProjectSettings.tsx
  36. 4 1
      dashboard/src/main/home/sidebar/ClusterSection.tsx
  37. 3 3
      dashboard/src/main/home/sidebar/Sidebar.tsx
  38. 1 0
      dashboard/src/shared/api.tsx
  39. 1 1
      dashboard/src/shared/common.tsx
  40. BIN
      porter-0.36.0.tgz

+ 4 - 0
dashboard/src/assets/left-arrow.svg

@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M4.25 12.2743L19.25 12.2743" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M10.2998 18.2987L4.2498 12.2747L10.2998 6.24969" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>

+ 4 - 3
dashboard/src/components/TitleSection.tsx

@@ -65,20 +65,21 @@ const StyledTitleSection = styled.div`
   margin-bottom: 15px;
   display: flex;
   align-items: center;
+  height: 35px;
 `;
 
 const Icon = styled.img<{ width: string }>`
-  width: ${(props) => props.width || "28px"};
+  width: ${(props) => props.width || "25px"};
   margin-right: 16px;
 `;
 
 const MaterialIcon = styled.span<{ width: string }>`
-  width: ${(props) => props.width || "28px"};
+  width: ${(props) => props.width || "25px"};
   margin-right: 16px;
 `;
 
 const StyledTitle = styled.div<{ capitalize: boolean }>`
-  font-size: 24px;
+  font-size: 21px;
   font-weight: 600;
   user-select: text;
   text-transform: ${(props) => (props.capitalize ? "capitalize" : "")};

+ 53 - 41
dashboard/src/components/expanded-object/Header.tsx

@@ -4,6 +4,8 @@ import styled from "styled-components";
 import backArrow from "assets/back_arrow.png";
 import TitleSection from "components/TitleSection";
 
+import leftArrow from "assets/left-arrow.svg";
+
 type Props = {
   last_updated: string;
   back_link: string;
@@ -26,26 +28,63 @@ const Header: React.FunctionComponent<Props> = (props) => {
   } = props;
 
   return (
-    <HeaderWrapper>
-      <BackButton to={back_link}>
-        <BackButtonImg src={backArrow} />
-      </BackButton>
-      <Title icon={icon} iconWidth="25px" materialIconClass={materialIconClass}>
-        {name}
-        <Flex>{inline_title_items}</Flex>
-      </Title>
+    <>
+      <BreadcrumbRow>
+        <Breadcrumb to={back_link}>
+          <ArrowIcon src={leftArrow} /><Wrap>Back</Wrap>
+        </Breadcrumb>
+      </BreadcrumbRow>
+      <HeaderWrapper>
+        <Title icon={icon} iconWidth="25px" materialIconClass={materialIconClass}>
+          {name}
+          <Flex>{inline_title_items}</Flex>
+        </Title>
 
-      {sub_title_items || (
-        <InfoWrapper>
-          <InfoText>Last updated {last_updated}</InfoText>
-        </InfoWrapper>
-      )}
-    </HeaderWrapper>
+        {sub_title_items || (
+          <InfoWrapper>
+            <InfoText>Last updated {last_updated}</InfoText>
+          </InfoWrapper>
+        )}
+      </HeaderWrapper>
+    </>
   );
 };
 
 export default Header;
 
+const Wrap = styled.div`
+  z-index: 999;
+`;
+
+const ArrowIcon = styled.img`
+  width: 15px;
+  margin-right: 8px;
+  opacity: 50%;
+`;
+
+const BreadcrumbRow = styled.div`
+  width: 100%;
+  display: flex;
+  justify-content: flex-start;
+`;
+
+const Breadcrumb = styled(DynamicLink)`
+  color: #aaaabb88;
+  font-size: 13px;
+  margin-bottom: 15px;
+  display: flex;
+  align-items: center;
+  margin-top: -10px;
+  z-index: 999;
+  padding: 5px;
+  padding-right: 7px;
+  border-radius: 5px;
+  cursor: pointer;
+  :hover {
+    background: #ffffff11;
+  }
+`;
+
 const HeaderWrapper = styled.div`
   position: relative;
   margin-bottom: 10px;
@@ -63,33 +102,6 @@ const InfoText = styled.span`
   color: #aaaabb66;
 `;
 
-const BackButton = styled(DynamicLink)`
-  position: absolute;
-  top: 0px;
-  right: 0px;
-  display: flex;
-  width: 36px;
-  cursor: pointer;
-  height: 36px;
-  align-items: center;
-  justify-content: center;
-  border: 1px solid #ffffff55;
-  border-radius: 100px;
-  background: #ffffff11;
-
-  :hover {
-    background: #ffffff22;
-    > img {
-      opacity: 1;
-    }
-  }
-`;
-
-const BackButtonImg = styled.img`
-  width: 16px;
-  opacity: 0.75;
-`;
-
 const Title = styled(TitleSection)`
   font-size: 16px;
   margin-top: 4px;

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

@@ -529,7 +529,7 @@ export default withRouter(withAuth(Home));
 const ViewWrapper = styled.div`
   height: 100%;
   width: 100vw;
-  padding-top: 10vh;
+  padding: 45px;
   overflow-y: auto;
   display: flex;
   flex: 1;
@@ -539,7 +539,7 @@ const ViewWrapper = styled.div`
 `;
 
 const DashboardWrapper = styled.div`
-  width: calc(85%);
+  width: 100%;
   min-width: 300px;
   height: fit-content;
 `;

+ 2 - 2
dashboard/src/main/home/cluster-dashboard/ClusterDashboard.tsx

@@ -191,7 +191,7 @@ class ClusterDashboard extends Component<PropsType, StateType> {
                 pushFiltered(this.props, "/launch", ["project_id"])
               }
             >
-              <i className="material-icons">add</i> Launch Template
+              <i className="material-icons">add</i> Launch template
             </Button>
           )}
           <SortFilterWrapper>{this.renderCommonFilters()}</SortFilterWrapper>
@@ -240,7 +240,7 @@ class ClusterDashboard extends Component<PropsType, StateType> {
                 pushFiltered(this.props, "/launch", ["project_id"])
               }
             >
-              <i className="material-icons">add</i> Launch Template
+              <i className="material-icons">add</i> Launch template
             </Button>
           )}
           <SortFilterWrapper>

+ 5 - 5
dashboard/src/main/home/cluster-dashboard/DashboardHeader.tsx

@@ -55,8 +55,9 @@ const Br = styled.div`
 
 const LineBreak = styled.div`
   width: calc(100% - 0px);
-  height: 2px;
-  background: #ffffff20;
+  height: 1px;
+  background: #494b4f;
+  width: 100%;
   margin: 10px 0px 35px;
 `;
 
@@ -66,11 +67,10 @@ const TopRow = styled.div`
 `;
 
 const Description = styled.div`
-  color: #aaaabb;
+  color: #8b949f;
   margin-top: 13px;
   margin-left: 2px;
   font-size: 13px;
-  line-height: 1.5em;
 `;
 
 const InfoLabel = styled.div`
@@ -78,7 +78,7 @@ const InfoLabel = styled.div`
   height: 20px;
   display: flex;
   align-items: center;
-  color: #7a838f;
+  color: #8b949f;
   font-size: 13px;
   > i {
     color: #8b949f;

+ 9 - 62
dashboard/src/main/home/cluster-dashboard/chart/Chart.tsx

@@ -48,7 +48,6 @@ const Chart: React.FunctionComponent<Props> = ({
   isJob,
   closeChartRedirectUrl,
 }) => {
-  const [expand, setExpand] = useState<boolean>(false);
   const [chartControllers, setChartControllers] = useState<any>([]);
   const [showDescription, setShowDescription] = useState(false);
   const context = useContext(Context);
@@ -120,9 +119,6 @@ const Chart: React.FunctionComponent<Props> = ({
 
   return (
     <StyledChart
-      onMouseEnter={() => setExpand(true)}
-      onMouseLeave={() => setExpand(false)}
-      expand={expand}
       onClick={() => {
         const cluster = context.currentCluster?.name;
         let route = `${isJob ? "/jobs" : "/applications"}/${cluster}/${
@@ -233,7 +229,7 @@ const BottomWrapper = styled.div`
   justify-content: space-between;
   align-items: center;
   padding-right: 11px;
-  margin-top: 12px;
+  margin-top: 3px;
 `;
 
 const TopRightContainer = styled.div`
@@ -365,66 +361,17 @@ const JobStatus = styled.span<{ status?: JobStatusType }>`
 `;
 
 const StyledChart = styled.div`
-  background: #26282f;
   cursor: pointer;
-  margin-bottom: 25px;
-  padding: 1px;
-  border-radius: 8px;
-  box-shadow: 0 4px 15px 0px #00000055;
+  margin-bottom: 15px;
+  padding-top: 2px;
+  padding-bottom: 13px;
   position: relative;
-  border: 2px solid #9eb4ff00;
   width: calc(100% + 2px);
   height: calc(100% + 2px);
-
-  animation: ${(props: { expand: boolean }) =>
-      props.expand ? "expand" : "shrink"}
-    0.12s;
-  animation-fill-mode: forwards;
-  animation-timing-function: ease-out;
-
-  @keyframes expand {
-    from {
-      width: calc(100% + 2px);
-      padding-top: 4px;
-      padding-bottom: 14px;
-      margin-left: 0px;
-      box-shadow: 0 4px 15px 0px #00000055;
-      padding-left: 1px;
-      margin-bottom: 25px;
-      margin-top: 0px;
-    }
-    to {
-      width: calc(100% + 22px);
-      padding-top: 7px;
-      padding-bottom: 20px;
-      margin-left: -10px;
-      box-shadow: 0 8px 20px 0px #00000030;
-      padding-left: 5px;
-      margin-bottom: 21px;
-      margin-top: -4px;
-    }
-  }
-
-  @keyframes shrink {
-    from {
-      width: calc(100% + 22px);
-      padding-top: 7px;
-      padding-bottom: 20px;
-      margin-left: -10px;
-      box-shadow: 0 8px 20px 0px #00000030;
-      padding-left: 5px;
-      margin-bottom: 21px;
-      margin-top: -4px;
-    }
-    to {
-      width: calc(100% + 2px);
-      padding-top: 4px;
-      padding-bottom: 14px;
-      margin-left: 0px;
-      box-shadow: 0 4px 15px 0px #00000055;
-      padding-left: 1px;
-      margin-bottom: 25px;
-      margin-top: 0px;
-    }
+  border-radius: 5px;
+  background: #262A30;
+  border: 1px solid #494b4f;
+  :hover {
+    border: 1px solid #7A7B80;
   }
 `;

+ 105 - 7
dashboard/src/main/home/cluster-dashboard/dashboard/Dashboard.tsx

@@ -4,6 +4,7 @@ import styled from "styled-components";
 import { Context } from "shared/Context";
 import TabSelector from "components/TabSelector";
 import TitleSection from "components/TitleSection";
+import api from "shared/api";
 
 import NodeList from "./NodeList";
 
@@ -15,6 +16,11 @@ import { useLocation } from "react-router";
 import { getQueryParam } from "shared/routing";
 import IncidentsTab from "./incidents/IncidentsTab";
 
+import CopyToClipboard from "components/CopyToClipboard";
+import Loading from "components/Loading";
+
+import { DetailedIngressError } from "shared/types";
+
 type TabEnum = "nodes" | "settings" | "namespaces" | "metrics" | "incidents";
 
 const tabOptions: {
@@ -35,6 +41,8 @@ export const Dashboard: React.FunctionComponent = () => {
   const [currentTabOptions, setCurrentTabOptions] = useState(tabOptions);
   const [isAuthorized] = useAuth();
   const location = useLocation();
+  const [ingressIp, setIngressIp] = useState(null);
+  const [ingressError, setIngressError] = useState(null);
 
   const context = useContext(Context);
   const renderTab = () => {
@@ -76,6 +84,68 @@ export const Dashboard: React.FunctionComponent = () => {
     setCurrentTab("nodes");
   }, [context.currentCluster]);
 
+  const renderIngressIp = (
+    ingressIp: string | undefined,
+    ingressError: DetailedIngressError
+  ) => {
+    if (typeof ingressIp !== "string") {
+      return (
+        <Url onClick={(e) => e.preventDefault()}>
+          <Loading />
+        </Url>
+      );
+    }
+
+    if (!ingressIp.length && ingressError) {
+      return (
+        <>
+          <Bolded>Ingress IP:</Bolded>
+          <span>{ingressError.message}</span>
+        </>
+      );
+    }
+
+    if (!ingressIp.length) {
+      return (
+        <>
+          <Bolded>Ingress IP:</Bolded>
+          <span>Ingress IP not available</span>
+        </>
+      );
+    }
+
+    return (
+      <CopyToClipboard
+        as={Url}
+        text={ingressIp}
+        wrapperProps={{ onClick: (e: any) => e.stopPropagation() }}
+      >
+        <Bolded>Ingress IP:</Bolded>
+        <span>{ingressIp}</span>
+        <i className="material-icons-outlined">content_copy</i>
+      </CopyToClipboard>
+    );
+  };
+
+  const updateClusterWithDetailedData = async () => {
+    try {
+      const res = await api.getCluster(
+        "<token>",
+        {},
+        { project_id: context.currentProject.id, cluster_id: context.currentCluster.id }
+      );
+      if (res.data) {
+        const { ingress_ip, ingress_error } = res.data;
+        setIngressIp(ingress_ip);
+        setIngressError(ingress_error);
+      }
+    } catch (error) {}
+  };
+
+  useEffect(() => {
+    updateClusterWithDetailedData();
+  }, []);
+
   return (
     <>
       <TitleSection>
@@ -92,7 +162,10 @@ export const Dashboard: React.FunctionComponent = () => {
           </InfoLabel>
         </TopRow>
         <Description>
-          Cluster settings for {context.currentCluster.name}
+          {renderIngressIp(
+            ingressIp,
+            ingressError
+          )}
         </Description>
       </InfoSection>
 
@@ -108,9 +181,9 @@ export const Dashboard: React.FunctionComponent = () => {
 };
 
 const DashboardIcon = styled.div`
-  height: 45px;
-  min-width: 45px;
-  width: 45px;
+  height: 35px;
+  min-width: 35px;
+  width: 35px;
   border-radius: 5px;
   margin-right: 17px;
   display: flex;
@@ -119,7 +192,7 @@ const DashboardIcon = styled.div`
   background: #676c7c;
   border: 2px solid #8e94aa;
   > i {
-    font-size: 22px;
+    font-size: 18px;
   }
 `;
 
@@ -129,7 +202,7 @@ const TopRow = styled.div`
 `;
 
 const Description = styled.div`
-  color: #aaaabb;
+  color: #8b949f;
   margin-top: 13px;
   margin-left: 2px;
   font-size: 13px;
@@ -140,7 +213,7 @@ const InfoLabel = styled.div`
   height: 20px;
   display: flex;
   align-items: center;
-  color: #7a838f;
+  color: #8b949f;
   font-size: 13px;
   > i {
     color: #8b949f;
@@ -155,3 +228,28 @@ const InfoSection = styled.div`
   margin-left: 0px;
   margin-bottom: 35px;
 `;
+
+const Url = styled.a`
+  font-size: 13px;
+  user-select: text;
+  font-weight: 400;
+  display: flex;
+  align-items: center;
+  cursor: pointer;
+  > i {
+    margin-left: 10px;
+    font-size: 15px;
+  }
+
+  > span {
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+  }
+`;
+
+const Bolded = styled.span`
+  color: #8b949f;
+  margin-right: 6px;
+  white-space: nowrap;
+`;

+ 1 - 1
dashboard/src/main/home/cluster-dashboard/dashboard/Metrics.tsx

@@ -44,7 +44,7 @@ const Metrics: React.FC = () => {
     if (selectedMetric && selectedRange && selectedIngress) {
       getMetrics();
     }
-  }, [selectedMetric, selectedRange, selectedIngress, selectedPercentile]);
+  }, [selectedMetric, selectedRange, selectedIngress, selectedPercentile, currentCluster]);
 
   useEffect(() => {
     Promise.all([

+ 39 - 6
dashboard/src/main/home/cluster-dashboard/dashboard/node-view/ExpandedNodeView.tsx

@@ -1,7 +1,7 @@
 import React, { useContext, useEffect, useMemo, useState } from "react";
 import { useHistory, useLocation, useParams } from "react-router";
 import styled from "styled-components";
-import backArrow from "assets/back_arrow.png";
+import leftArrow from "assets/left-arrow.svg";
 import api from "shared/api";
 import { Context } from "shared/Context";
 
@@ -49,8 +49,6 @@ export const ExpandedNodeView = () => {
           setNode(res.data);
         }
       });
-
-    return () => (isSubscribed = false);
   }, [nodeId, currentCluster.id, currentProject.id]);
 
   const closeNodeView = () => {
@@ -92,10 +90,12 @@ export const ExpandedNodeView = () => {
 
   return (
     <StyledExpandedNodeView>
+      <BreadcrumbRow>
+        <Breadcrumb onClick={closeNodeView}>
+          <ArrowIcon src={leftArrow} /><Wrap>Back</Wrap>
+        </Breadcrumb>
+      </BreadcrumbRow>
       <HeaderWrapper>
-        <BackButton onClick={closeNodeView}>
-          <BackButtonImg src={backArrow} />
-        </BackButton>
         <TitleSection icon={nodePng}>
           {nodeId}
           <InstanceType>{instanceType}</InstanceType>
@@ -121,6 +121,39 @@ export const ExpandedNodeView = () => {
 
 export default ExpandedNodeView;
 
+const ArrowIcon = styled.img`
+  width: 15px;
+  margin-right: 8px;
+  opacity: 50%;
+`;
+
+const BreadcrumbRow = styled.div`
+  width: 100%;
+  display: flex;
+  justify-content: flex-start;
+`;
+
+const Breadcrumb = styled.div`
+  color: #aaaabb88;
+  font-size: 13px;
+  margin-bottom: 15px;
+  display: flex;
+  align-items: center;
+  margin-top: -10px;
+  z-index: 999;
+  padding: 5px;
+  padding-right: 7px;
+  border-radius: 5px;
+  cursor: pointer;
+  :hover {
+    background: #ffffff11;
+  }
+`;
+
+const Wrap = styled.div`
+  z-index: 999;
+`;
+
 const BackButton = styled.div`
   position: absolute;
   top: 0px;

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

@@ -207,7 +207,7 @@ export default class CreateEnvGroup extends Component<PropsType, StateType> {
           />
           <SaveButton
             disabled={this.isDisabled()}
-            text="Create Env Group"
+            text="Create env group"
             onClick={this.onSubmit}
             status={
               this.isDisabled()

+ 10 - 59
dashboard/src/main/home/cluster-dashboard/env-groups/EnvGroup.tsx

@@ -81,7 +81,7 @@ const BottomWrapper = styled.div`
   justify-content: space-between;
   align-items: center;
   padding-right: 11px;
-  margin-top: 12px;
+  margin-top: 3px;
 `;
 
 const Version = styled.div`
@@ -108,7 +108,7 @@ const InfoWrapper = styled.div`
 const LastDeployed = styled.div`
   font-size: 13px;
   margin-left: 14px;
-  margin-top: -1px;
+  margin-bottom: -1px;
   display: flex;
   align-items: center;
   color: #aaaabb66;
@@ -195,66 +195,17 @@ const Title = styled.div`
 `;
 
 const StyledEnvGroup = styled.div`
-  background: #26282f;
   cursor: pointer;
-  margin-bottom: 25px;
-  padding: 1px;
-  border-radius: 8px;
-  box-shadow: 0 4px 15px 0px #00000055;
+  margin-bottom: 15px;
+  padding-top: 2px;
+  padding-bottom: 13px;
   position: relative;
-  border: 2px solid #9eb4ff00;
   width: calc(100% + 2px);
   height: calc(100% + 2px);
-
-  animation: ${(props: { expand: boolean }) =>
-      props.expand ? "expand" : "shrink"}
-    0.12s;
-  animation-fill-mode: forwards;
-  animation-timing-function: ease-out;
-
-  @keyframes expand {
-    from {
-      width: calc(100% + 2px);
-      padding-top: 4px;
-      padding-bottom: 14px;
-      margin-left: 0px;
-      box-shadow: 0 4px 15px 0px #00000055;
-      padding-left: 1px;
-      margin-bottom: 25px;
-      margin-top: 0px;
-    }
-    to {
-      width: calc(100% + 22px);
-      padding-top: 7px;
-      padding-bottom: 20px;
-      margin-left: -10px;
-      box-shadow: 0 8px 20px 0px #00000030;
-      padding-left: 5px;
-      margin-bottom: 21px;
-      margin-top: -4px;
-    }
-  }
-
-  @keyframes shrink {
-    from {
-      width: calc(100% + 22px);
-      padding-top: 7px;
-      padding-bottom: 20px;
-      margin-left: -10px;
-      box-shadow: 0 8px 20px 0px #00000030;
-      padding-left: 5px;
-      margin-bottom: 21px;
-      margin-top: -4px;
-    }
-    to {
-      width: calc(100% + 2px);
-      padding-top: 4px;
-      padding-bottom: 14px;
-      margin-left: 0px;
-      box-shadow: 0 4px 15px 0px #00000055;
-      padding-left: 1px;
-      margin-bottom: 25px;
-      margin-top: 0px;
-    }
+  border-radius: 5px;
+  background: #262A30;
+  border: 1px solid #494b4f;
+  :hover {
+    border: 1px solid #7A7B80;
   }
 `;

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

@@ -64,7 +64,7 @@ class EnvGroupDashboard extends Component<PropsType, StateType> {
                   this.setState({ createEnvMode: !this.state.createEnvMode })
                 }
               >
-                <i className="material-icons">add</i> Create Env Group
+                <i className="material-icons">add</i> Create env group
               </Button>
             )}
             <SortFilterWrapper>

+ 39 - 3
dashboard/src/main/home/cluster-dashboard/env-groups/ExpandedEnvGroup.tsx

@@ -9,6 +9,7 @@ import styled, { keyframes } from "styled-components";
 import backArrow from "assets/back_arrow.png";
 import key from "assets/key.svg";
 import loading from "assets/loading.gif";
+import leftArrow from "assets/left-arrow.svg";
 
 import { ClusterType } from "shared/types";
 import { Context } from "shared/Context";
@@ -418,10 +419,12 @@ export const ExpandedEnvGroupFC = ({
 
   return (
     <StyledExpandedChart>
+      <BreadcrumbRow>
+        <Breadcrumb onClick={closeExpanded}>
+          <ArrowIcon src={leftArrow} /><Wrap>Back</Wrap>
+        </Breadcrumb>
+      </BreadcrumbRow>
       <HeaderWrapper>
-        <BackButton onClick={closeExpanded}>
-          <BackButtonImg src={backArrow} />
-        </BackButton>
         <TitleSection icon={key} iconWidth="33px">
           {envGroup.name}
           <TagWrapper>
@@ -628,6 +631,39 @@ const ApplicationsList = ({ envGroup }: { envGroup: EditableEnvGroup }) => {
   );
 };
 
+const ArrowIcon = styled.img`
+  width: 15px;
+  margin-right: 8px;
+  opacity: 50%;
+`;
+
+const BreadcrumbRow = styled.div`
+  width: 100%;
+  display: flex;
+  justify-content: flex-start;
+`;
+
+const Breadcrumb = styled.div`
+  color: #aaaabb88;
+  font-size: 13px;
+  margin-bottom: 15px;
+  display: flex;
+  align-items: center;
+  margin-top: -10px;
+  z-index: 999;
+  padding: 5px;
+  padding-right: 7px;
+  border-radius: 5px;
+  cursor: pointer;
+  :hover {
+    background: #ffffff11;
+  }
+`;
+
+const Wrap = styled.div`
+  z-index: 999;
+`;
+
 const HeadingWrapper = styled.div`
   display: flex;
   margin-bottom: 15px;

+ 34 - 109
dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedChart.tsx

@@ -1,9 +1,9 @@
 import React, { useCallback, useContext, useEffect, useState } from "react";
 import styled from "styled-components";
 import yaml from "js-yaml";
-import backArrow from "assets/back_arrow.png";
 import _, { cloneDeep } from "lodash";
 import loadingSrc from "assets/loading.gif";
+import leftArrow from "assets/left-arrow.svg";
 
 import { ChartType, ClusterType, ResourceType } from "shared/types";
 import { Context } from "shared/Context";
@@ -760,10 +760,12 @@ const ExpandedChart: React.FC<Props> = (props) => {
         />
       ) : (
         <StyledExpandedChart>
+          <BreadcrumbRow>
+            <Breadcrumb onClick={props.closeChart}>
+              <ArrowIcon src={leftArrow} /><Wrap>Back</Wrap>
+            </Breadcrumb>
+          </BreadcrumbRow>
           <HeaderWrapper>
-            <BackButton onClick={props.closeChart}>
-              <BackButtonImg src={backArrow} />
-            </BackButton>
             <TitleSection
               icon={currentChart.chart.metadata.icon}
               iconWidth="33px"
@@ -891,51 +893,41 @@ const ExpandedChart: React.FC<Props> = (props) => {
 
 export default ExpandedChart;
 
-const RepositoryName = styled.div`
-  white-space: nowrap;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  max-width: 390px;
-  position: relative;
-  margin-right: 3px;
+const ArrowIcon = styled.img`
+  width: 15px;
+  margin-right: 8px;
+  opacity: 50%;
 `;
 
-const Tooltip = styled.div`
-  position: absolute;
-  left: -40px;
-  top: 28px;
-  min-height: 18px;
-  max-width: calc(700px);
-  padding: 5px 7px;
-  background: #272731;
-  z-index: 999;
-  color: white;
-  font-size: 12px;
-  font-family: "Work Sans", sans-serif;
-  outline: 1px solid #ffffff55;
-  opacity: 0;
-  animation: faded-in 0.2s 0.15s;
-  animation-fill-mode: forwards;
-  @keyframes faded-in {
-    from {
-      opacity: 0;
-    }
-    to {
-      opacity: 1;
-    }
-  }
+const BreadcrumbRow = styled.div`
+  width: 100%;
+  display: flex;
+  justify-content: flex-start;
 `;
 
-const TextWrap = styled.div``;
-
-const LoadingWrapper = styled.div`
-  width: 100%;
-  height: 200px;
+const Breadcrumb = styled.div`
+  color: #aaaabb88;
+  font-size: 13px;
+  margin-bottom: 15px;
   display: flex;
   align-items: center;
-  justify-content: center;
+  margin-top: -10px;
+  z-index: 999;
+  padding: 5px;
+  padding-right: 7px;
+  border-radius: 5px;
+  cursor: pointer;
+  :hover {
+    background: #ffffff11;
+  }
+`;
+
+const Wrap = styled.div`
+  z-index: 999;
 `;
 
+const TextWrap = styled.div``;
+
 const LineBreak = styled.div`
   width: calc(100% - 0px);
   height: 2px;
@@ -945,34 +937,7 @@ const LineBreak = styled.div`
 
 const BodyWrapper = styled.div`
   position: relative;
-  margin-bottom: 120px;
-`;
-
-const BackButton = styled.div`
-  position: absolute;
-  top: 0px;
-  right: 0px;
-  display: flex;
-  width: 36px;
-  cursor: pointer;
-  height: 36px;
-  align-items: center;
-  justify-content: center;
-  border: 1px solid #ffffff55;
-  border-radius: 100px;
-  background: #ffffff11;
-
-  :hover {
-    background: #ffffff22;
-    > img {
-      opacity: 1;
-    }
-  }
-`;
-
-const BackButtonImg = styled.img`
-  width: 16px;
-  opacity: 0.75;
+  margin-bottom: 50px;
 `;
 
 const Header = styled.div`
@@ -1113,29 +1078,8 @@ const NamespaceTag = styled.div`
   border-bottom-left-radius: 0px;
 `;
 
-const Icon = styled.img`
-  width: 100%;
-`;
-
-const IconWrapper = styled.div`
-  color: #efefef;
-  font-size: 16px;
-  height: 20px;
-  width: 20px;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  border-radius: 3px;
-  margin-right: 12px;
-
-  > i {
-    font-size: 20px;
-  }
-`;
-
 const StyledExpandedChart = styled.div`
   width: 100%;
-  overflow: hidden;
   z-index: 0;
   animation: fadeIn 0.3s;
   animation-timing-function: ease-out;
@@ -1153,25 +1097,6 @@ const StyledExpandedChart = styled.div`
   }
 `;
 
-const DeploymentImageContainer = styled.div`
-  height: 20px;
-  font-size: 13px;
-  position: relative;
-  display: flex;
-  margin-left: 15px;
-  margin-bottom: -3px;
-  align-items: center;
-  font-weight: 400;
-  justify-content: center;
-  color: #ffffff66;
-  padding-left: 5px;
-`;
-
-const DeploymentTypeIcon = styled(Icon)`
-  width: 20px;
-  margin-right: 10px;
-`;
-
 const A = styled.a`
   color: #8590ff;
   text-decoration: underline;

+ 1 - 2
dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedChartWrapper.tsx

@@ -156,6 +156,5 @@ export default withRouter(ExpandedChartWrapper);
 
 const LoadingWrapper = styled.div`
   width: 100%;
-  height: 100%;
-  margin-top: -50px;
+  height: 100vh;
 `;

+ 84 - 47
dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedJobChart.tsx

@@ -2,7 +2,7 @@ import React, { useContext, useMemo, useState } from "react";
 import styled from "styled-components";
 import yaml from "js-yaml";
 
-import backArrow from "assets/back_arrow.png";
+import leftArrow from "assets/left-arrow.svg";
 import { cloneDeep, set } from "lodash";
 import loading from "assets/loading.gif";
 
@@ -426,54 +426,91 @@ const ExpandedJobHeader: React.FC<{
   setDisableForm,
   disableRevisions,
 }) => (
-  <HeaderWrapper>
-    <BackButton onClick={closeChart}>
-      <BackButtonImg src={backArrow} />
-    </BackButton>
-    <TitleSection icon={chart?.chart.metadata.icon} iconWidth="33px">
-      {chart?.name}
-      <DeploymentType currentChart={chart} />
-      <TagWrapper>
-        Namespace <NamespaceTag>{chart.namespace}</NamespaceTag>
-      </TagWrapper>
-    </TitleSection>
-    {chart?.config?.description ? (
-      <Description>{chart?.config?.description}</Description>
-    ) : null}
-
-    <InfoWrapper>
-      <LastDeployed>
-        Run {jobs?.length} times <Dot>•</Dot>Last template update at
-        {" " + readableDate(chart.info.last_deployed)}
-      </LastDeployed>
-    </InfoWrapper>
-    {!disableRevisions ? (
-      <RevisionSection
-        chart={chart}
-        refreshChart={() => refreshChart()}
-        setRevision={(chart, isCurrent) => {
-          loadChartWithSpecificRevision(chart?.version);
-          setDisableForm(!isCurrent);
-        }}
-        forceRefreshRevisions={false}
-        refreshRevisionsOff={() => {}}
-        shouldUpdate={
-          chart?.latest_version &&
-          chart?.latest_version !== chart?.chart.metadata.version
-        }
-        latestVersion={chart?.latest_version}
-        upgradeVersion={(_version, cb) => {
-          upgradeChart().then(() => {
-            if (typeof cb === "function") {
-              cb();
-            }
-          });
-        }}
-      />
-    ) : null}
-  </HeaderWrapper>
+  <>
+    <BreadcrumbRow>
+      <Breadcrumb onClick={closeChart}>
+        <ArrowIcon src={leftArrow} /><Wrap>Back</Wrap>
+      </Breadcrumb>
+    </BreadcrumbRow>
+    <HeaderWrapper>
+      <TitleSection icon={chart?.chart.metadata.icon} iconWidth="33px">
+        {chart?.name}
+        <DeploymentType currentChart={chart} />
+        <TagWrapper>
+          Namespace <NamespaceTag>{chart.namespace}</NamespaceTag>
+        </TagWrapper>
+      </TitleSection>
+      {chart?.config?.description ? (
+        <Description>{chart?.config?.description}</Description>
+      ) : null}
+
+      <InfoWrapper>
+        <LastDeployed>
+          Run {jobs?.length} times <Dot>•</Dot>Last template update at
+          {" " + readableDate(chart.info.last_deployed)}
+        </LastDeployed>
+      </InfoWrapper>
+      {!disableRevisions ? (
+        <RevisionSection
+          chart={chart}
+          refreshChart={() => refreshChart()}
+          setRevision={(chart, isCurrent) => {
+            loadChartWithSpecificRevision(chart?.version);
+            setDisableForm(!isCurrent);
+          }}
+          forceRefreshRevisions={false}
+          refreshRevisionsOff={() => {}}
+          shouldUpdate={
+            chart?.latest_version &&
+            chart?.latest_version !== chart?.chart.metadata.version
+          }
+          latestVersion={chart?.latest_version}
+          upgradeVersion={(_version, cb) => {
+            upgradeChart().then(() => {
+              if (typeof cb === "function") {
+                cb();
+              }
+            });
+          }}
+        />
+      ) : null}
+    </HeaderWrapper>
+  </>
 );
 
+const ArrowIcon = styled.img`
+  width: 15px;
+  margin-right: 8px;
+  opacity: 50%;
+`;
+
+const BreadcrumbRow = styled.div`
+  width: 100%;
+  display: flex;
+  justify-content: flex-start;
+`;
+
+const Breadcrumb = styled.div`
+  color: #aaaabb88;
+  font-size: 13px;
+  margin-bottom: 15px;
+  display: flex;
+  align-items: center;
+  margin-top: -10px;
+  z-index: 999;
+  padding: 5px;
+  padding-right: 7px;
+  border-radius: 5px;
+  cursor: pointer;
+  :hover {
+    background: #ffffff11;
+  }
+`;
+
+const Wrap = styled.div`
+  z-index: 999;
+`;
+
 const RunsDescription = styled.div`
   color: #ffffff;
   font-size: 13px;

+ 39 - 4
dashboard/src/main/home/cluster-dashboard/expanded-chart/jobs/ExpandedJobRun.tsx

@@ -2,7 +2,7 @@ import React, { useContext, useEffect, useState } from "react";
 import { get, isEmpty } from "lodash";
 import styled from "styled-components";
 
-import backArrow from "assets/back_arrow.png";
+import leftArrow from "assets/left-arrow.svg";
 import KeyValueArray from "components/form-components/KeyValueArray";
 import Loading from "components/Loading";
 import TabRegion from "components/TabRegion";
@@ -197,10 +197,12 @@ const ExpandedJobRun = ({
 
   return (
     <StyledExpandedChart>
+      <BreadcrumbRow>
+        <Breadcrumb onClick={onClose}>
+          <ArrowIcon src={leftArrow} /><Wrap>Back</Wrap>
+        </Breadcrumb>
+      </BreadcrumbRow>
       <HeaderWrapper>
-        <BackButton onClick={() => onClose()}>
-          <BackButtonImg src={backArrow} />
-        </BackButton>
         <TitleSection icon={currentChart.chart.metadata.icon} iconWidth="33px">
           {chart.name} <Gray>at {readableDate(run.status.startTime)}</Gray>
         </TitleSection>
@@ -261,6 +263,39 @@ const ExpandedJobRun = ({
 
 export default ExpandedJobRun;
 
+const ArrowIcon = styled.img`
+  width: 15px;
+  margin-right: 8px;
+  opacity: 50%;
+`;
+
+const BreadcrumbRow = styled.div`
+  width: 100%;
+  display: flex;
+  justify-content: flex-start;
+`;
+
+const Breadcrumb = styled.div`
+  color: #aaaabb88;
+  font-size: 13px;
+  margin-bottom: 15px;
+  display: flex;
+  align-items: center;
+  margin-top: -10px;
+  z-index: 999;
+  padding: 5px;
+  padding-right: 7px;
+  border-radius: 5px;
+  cursor: pointer;
+  :hover {
+    background: #ffffff11;
+  }
+`;
+
+const Wrap = styled.div`
+  z-index: 999;
+`;
+
 const Row = styled.div`
   margin-top: 20px;
 `;

+ 4 - 12
dashboard/src/main/home/cluster-dashboard/preview-environments/environments/EnvironmentCard.tsx

@@ -168,17 +168,16 @@ const OptionWrapper = styled.div`
 const EnvironmentCardWrapper = styled(DynamicLink)`
   display: flex;
   color: #ffffff;
-  background: #2b2e3699;
   justify-content: space-between;
-  border-radius: 5px;
   cursor: pointer;
   height: 75px;
   padding: 12px;
   padding-left: 14px;
-  border: 1px solid #ffffff0f;
-
+  border-radius: 5px;
+  background: #262A30;
+  border: 1px solid #494b4f;
   :hover {
-    border: 1px solid #ffffff3c;
+    border: 1px solid #7A7B80;
   }
   animation: fadeIn 0.5s;
   @keyframes fadeIn {
@@ -278,13 +277,6 @@ const DeleteButton = styled(Button)`
   }}
 `;
 
-const CancelButton = styled(Button)`
-  background: #616feecc;
-  :hover {
-    background: #505edddd;
-  }
-`;
-
 const ActionWrapper = styled.div`
   display: flex;
   align-items: center;

+ 1 - 1
dashboard/src/main/home/cluster-dashboard/stacks/Dashboard.tsx

@@ -42,7 +42,7 @@ const Dashboard = () => {
       <Action.Row>
         <Action.Button to={"/stacks/launch"}>
           <i className="material-icons">add</i>
-          Create Stack
+          Create stack
         </Action.Button>
         <FilterWrapper>
           <StyledSortSelector>

+ 40 - 5
dashboard/src/main/home/cluster-dashboard/stacks/ExpandedStack/ExpandedStack.tsx

@@ -3,7 +3,7 @@ import Placeholder from "components/Placeholder";
 import TabSelector from "components/TabSelector";
 import TitleSection from "components/TitleSection";
 import React, { useContext, useState } from "react";
-import backArrow from "assets/back_arrow.png";
+import leftArrow from "assets/left-arrow.svg";
 import { useParams, useRouteMatch } from "react-router";
 import api from "shared/api";
 import { Context } from "shared/Context";
@@ -94,10 +94,12 @@ const ExpandedStack = () => {
 
   return (
     <div>
+      <BreadcrumbRow>
+        <Breadcrumb to="/stacks">
+          <ArrowIcon src={leftArrow} /><Wrap>Back</Wrap>
+        </Breadcrumb>
+      </BreadcrumbRow>
       <StackTitleWrapper>
-        <BackButton to="/stacks">
-          <BackButtonImg src={backArrow} />
-        </BackButton>
         <TitleSection materialIconClass="material-icons-outlined" icon={"lan"}>
           {stack.name}
         </TitleSection>
@@ -205,7 +207,7 @@ const ExpandedStack = () => {
                 <Action.Row>
                   <Action.Button to={`${url}/new-env-group`}>
                     <i className="material-icons">add</i>
-                    Create Env Group
+                    Create env group
                   </Action.Button>
                 </Action.Row>
                 <EnvGroups stack={stack} />
@@ -238,6 +240,39 @@ const ExpandedStack = () => {
 
 export default ExpandedStack;
 
+const ArrowIcon = styled.img`
+  width: 15px;
+  margin-right: 8px;
+  opacity: 50%;
+`;
+
+const BreadcrumbRow = styled.div`
+  width: 100%;
+  display: flex;
+  justify-content: flex-start;
+`;
+
+const Breadcrumb = styled(DynamicLink)`
+  color: #aaaabb88;
+  font-size: 13px;
+  margin-bottom: 15px;
+  display: flex;
+  align-items: center;
+  margin-top: -10px;
+  z-index: 999;
+  padding: 5px;
+  padding-right: 7px;
+  border-radius: 5px;
+  cursor: pointer;
+  :hover {
+    background: #ffffff11;
+  }
+`;
+
+const Wrap = styled.div`
+  z-index: 999;
+`;
+
 const PaddingBottom = styled.div`
   width: 100%;
   height: 150px;

+ 1 - 1
dashboard/src/main/home/cluster-dashboard/stacks/_StackList.tsx

@@ -117,7 +117,7 @@ const StackList = ({
       <Placeholder height="250px">
         <div>
           <h3>No stacks found</h3>
-          <p>You can create a stack by clicking the "Create Stack" button.</p>
+          <p>You can create a stack by clicking the "Create stack" button.</p>
         </div>
       </Placeholder>
     );

+ 2 - 2
dashboard/src/main/home/cluster-dashboard/stacks/launch/Overview.tsx

@@ -220,13 +220,13 @@ const Overview = () => {
 
       <SubmitButton
         disabled={!isValid || submitButtonStatus !== ""}
-        text="Create Stack"
+        text="Create stack"
         onClick={handleSubmit}
         clearPosition
         statusPosition="left"
         status={submitButtonStatus}
       >
-        Create Stack
+        Create stack
       </SubmitButton>
     </>
   );

+ 4 - 4
dashboard/src/main/home/cluster-dashboard/stacks/launch/components/styles.tsx

@@ -11,14 +11,14 @@ export const Card = {
   Wrapper: styled.div<{ variant?: "clickable" | "unclickable" }>`
     display: flex;
     color: #ffffff;
-    background: #2b2e3699;
     justify-content: space-between;
-    border-radius: 5px;
     height: 75px;
     padding: 12px;
     padding-left: 14px;
-    border: 1px solid #ffffff0f;
     align-items: center;
+    border-radius: 5px;
+    background: #262A30;
+    border: 1px solid #494b4f;
 
     ${(props) => {
       if (props.variant === "unclickable") {
@@ -33,7 +33,7 @@ export const Card = {
       return `
         cursor: pointer;
         :hover {
-          border: 1px solid #ffffff3c;
+          border: 1px solid #7A7B80;
         }
       `;
     }}

+ 72 - 154
dashboard/src/main/home/dashboard/ClusterList.tsx

@@ -6,16 +6,14 @@ import api from "shared/api";
 import {
   ClusterType,
   DetailedClusterType,
-  DetailedIngressError,
 } from "shared/types";
 import Helper from "components/form-components/Helper";
 import { pushFiltered } from "shared/routing";
 
 import { RouteComponentProps, withRouter } from "react-router";
 
-import CopyToClipboard from "components/CopyToClipboard";
-import Loading from "components/Loading";
 import Modal from "../modals/Modal";
+import Heading from "components/form-components/Heading";
 
 type PropsType = RouteComponentProps & {
   currentCluster: ClusterType;
@@ -59,10 +57,6 @@ class Templates extends Component<PropsType, StateType> {
 
       if (res.data) {
         this.setState({ clusters: res.data, loading: false, error: "" });
-
-        this.state.clusters.forEach((cluster) => {
-          this.updateClusterWithDetailedData(cluster.id);
-        });
       } else {
         this.setState({ loading: false, error: "Response data missing" });
       }
@@ -71,90 +65,67 @@ class Templates extends Component<PropsType, StateType> {
     }
   };
 
-  updateClusterWithDetailedData = async (clusterId: number) => {
-    try {
-      const currentClusterIndex = this.state.clusters.findIndex(
-        (cluster) => cluster.id === clusterId
-      );
-      const res = await api.getCluster(
-        "<token>",
-        {},
-        { project_id: this.context.currentProject.id, cluster_id: clusterId }
-      );
-      if (res.data) {
-        this.setState((prevState) => {
-          const currentCluster = prevState.clusters[currentClusterIndex];
-          prevState.clusters.splice(currentClusterIndex, 1, {
-            ...currentCluster,
-            ingress_ip: res.data.ingress_ip,
-            ingress_error: res.data.ingress_error,
-          });
-          return prevState;
-        });
-      }
-    } catch (error) {}
-  };
-
   renderIcon = () => {
     return (
       <DashboardIcon>
-        <i className="material-icons">device_hub</i>
+        <svg
+          width="16"
+          height="16"
+          viewBox="0 0 19 19"
+          fill="none"
+          xmlns="http://www.w3.org/2000/svg"
+        >
+          <path
+            d="M15.207 12.4403C16.8094 12.4403 18.1092 11.1414 18.1092 9.53907C18.1092 7.93673 16.8094 6.63782 15.207 6.63782"
+            stroke="white"
+            stroke-width="1.5"
+            stroke-linecap="round"
+            stroke-linejoin="round"
+          />
+          <path
+            d="M3.90217 12.4403C2.29983 12.4403 1 11.1414 1 9.53907C1 7.93673 2.29983 6.63782 3.90217 6.63782"
+            stroke="white"
+            stroke-width="1.5"
+            stroke-linecap="round"
+            stroke-linejoin="round"
+          />
+          <path
+            fill-rule="evenodd"
+            clip-rule="evenodd"
+            d="M9.54993 13.4133C7.4086 13.4133 5.69168 11.6964 5.69168 9.55417C5.69168 7.41284 7.4086 5.69592 9.54993 5.69592C11.6913 5.69592 13.4082 7.41284 13.4082 9.55417C13.4082 11.6964 11.6913 13.4133 9.54993 13.4133Z"
+            stroke="white"
+            stroke-width="1.5"
+            stroke-linecap="round"
+            stroke-linejoin="round"
+          />
+          <path
+            d="M6.66895 15.207C6.66895 16.8094 7.96787 18.1092 9.5702 18.1092C11.1725 18.1092 12.4715 16.8094 12.4715 15.207"
+            stroke="white"
+            stroke-width="1.5"
+            stroke-linecap="round"
+            stroke-linejoin="round"
+          />
+          <path
+            d="M6.66895 3.90217C6.66895 2.29983 7.96787 1 9.5702 1C11.1725 1 12.4715 2.29983 12.4715 3.90217"
+            stroke="white"
+            stroke-width="1.5"
+            stroke-linecap="round"
+            stroke-linejoin="round"
+          />
+          <path
+            fill-rule="evenodd"
+            clip-rule="evenodd"
+            d="M5.69591 9.54996C5.69591 7.40863 7.41283 5.69171 9.55508 5.69171C11.6964 5.69171 13.4133 7.40863 13.4133 9.54996C13.4133 11.6913 11.6964 13.4082 9.55508 13.4082C7.41283 13.4082 5.69591 11.6913 5.69591 9.54996Z"
+            stroke="white"
+            stroke-width="1.5"
+            stroke-linecap="round"
+            stroke-linejoin="round"
+          />
+        </svg>
       </DashboardIcon>
     );
   };
 
-  renderIngressIp = (
-    clusterId: number,
-    ingressIp: string | undefined,
-    ingressError: DetailedIngressError
-  ) => {
-    if (typeof ingressIp !== "string") {
-      return (
-        <Url onClick={(e) => e.preventDefault()}>
-          <Loading />
-        </Url>
-      );
-    }
-
-    if (!ingressIp.length && ingressError) {
-      return (
-        <>
-          <Url
-            onClick={(e) => {
-              e.stopPropagation();
-              this.setState({ showErrorModal: { clusterId, show: true } });
-            }}
-          >
-            <Bolded>Ingress IP:</Bolded>
-            <span>{ingressError.message}</span>
-            <i className="material-icons">launch</i>
-          </Url>
-        </>
-      );
-    }
-
-    if (!ingressIp.length) {
-      return (
-        <Url>
-          <Bolded>Ingress IP:</Bolded>
-          <span>Ingress IP not available</span>
-        </Url>
-      );
-    }
-
-    return (
-      <CopyToClipboard
-        as={Url}
-        text={ingressIp}
-        wrapperProps={{ onClick: (e: any) => e.stopPropagation() }}
-      >
-        <Bolded>Ingress IP:</Bolded>
-        <span>{ingressIp}</span>
-        <i className="material-icons-outlined">content_copy</i>
-      </CopyToClipboard>
-    );
-  };
-
   renderClusters = () => {
     return this.state.clusters.map(
       (cluster: DetailedClusterType, i: number) => {
@@ -168,15 +139,8 @@ class Templates extends Component<PropsType, StateType> {
             }}
             key={i}
           >
-            <TitleContainer>
-              {this.renderIcon()}
-              <TemplateTitle>{cluster.name}</TemplateTitle>
-            </TitleContainer>
-            {this.renderIngressIp(
-              cluster.id,
-              cluster.ingress_ip,
-              cluster.ingress_error
-            )}
+            {this.renderIcon()}
+            <TemplateTitle>{cluster.name}</TemplateTitle>
           </TemplateBlock>
         );
       }
@@ -209,7 +173,7 @@ class Templates extends Component<PropsType, StateType> {
   render() {
     return (
       <StyledClusterList>
-        <Helper>Clusters connected to this project:</Helper>
+        {/* <Heading isAtTop>Connected clusters</Heading> */}
         <TemplateList>{this.renderClusters()}</TemplateList>
         {this.renderErrorModal()}
       </StyledClusterList>
@@ -238,40 +202,30 @@ const CodeBlock = styled.span`
 `;
 
 const StyledClusterList = styled.div`
-  margin-top: -17px;
+  margin-top: -7px;
   padding-left: 2px;
   overflow: visible;
 `;
 
-const TitleContainer = styled.div`
-  display: flex;
-  width: 100%;
-  flex-direction: column;
-  align-items: center;
-`;
 const DashboardIcon = styled.div`
   position: relative;
-  height: 45px;
-  min-width: 45px;
-  width: 45px;
-  border-radius: 5px;
+  height: 25px;
+  min-width: 25px;
+  width: 25px;
+  border-radius: 200px;
+  margin-right: 15px;
   display: flex;
   align-items: center;
   justify-content: center;
   background: #676c7c;
-  border: 2px solid #8e94aa;
-  margin-bottom: 10px;
+  border: 1px solid #8e94aa;
   > i {
     font-size: 22px;
   }
 `;
 
 const TemplateTitle = styled.div`
-  margin-bottom: 0px;
-  margin-top: 13px;
-  width: 100%;
   text-align: center;
-  font-size: 14px;
   white-space: nowrap;
   overflow: hidden;
   white-space: nowrap;
@@ -279,25 +233,22 @@ const TemplateTitle = styled.div`
 `;
 
 const TemplateBlock = styled.div`
-  border: 1px solid #ffffff00;
   align-items: center;
   user-select: none;
-  border-radius: 8px;
   display: flex;
   font-size: 13px;
   font-weight: 500;
-  padding: 35px;
-  flex-direction: column;
+  padding: 15px;
+  margin-bottom: 20px;
   align-item: center;
-  justify-content: space-between;
-  height: 192px;
   cursor: pointer;
   color: #ffffff;
   position: relative;
-  background: #26282f;
-  box-shadow: 0 4px 15px 0px #00000055;
+  border-radius: 5px;
+  background: #262A30;
+  border: 1px solid #494b4f;
   :hover {
-    background: #ffffff11;
+    border: 1px solid #7A7B80;
   }
 
   animation: fadeIn 0.3s 0s;
@@ -314,37 +265,4 @@ const TemplateBlock = styled.div`
 const TemplateList = styled.div`
   overflow-y: auto;
   overflow: visible;
-  margin-top: 32px;
-  padding-bottom: 150px;
-  display: grid;
-  grid-column-gap: 25px;
-  grid-row-gap: 25px;
-  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
-`;
-
-const Url = styled.a`
-  width: 100%;
-  font-size: 13px;
-  user-select: text;
-  font-weight: 400;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  > i {
-    margin-left: 10px;
-    font-size: 15px;
-  }
-
-  > span {
-    overflow: hidden;
-    white-space: nowrap;
-    text-overflow: ellipsis;
-  }
-`;
-
-const Bolded = styled.div`
-  font-weight: 500;
-  color: #ffffff44;
-  margin-right: 6px;
-  white-space: nowrap;
-`;
+`;

+ 11 - 12
dashboard/src/main/home/dashboard/Dashboard.tsx

@@ -149,16 +149,16 @@ class Dashboard extends Component<PropsType, StateType> {
     let { currentProject, capabilities } = this.context;
     let { onShowProjectSettings } = this;
 
-    let tabOptions = [{ label: "Project Overview", value: "overview" }];
+    let tabOptions = [{ label: "Connected clusters", value: "overview" }];
 
     if (this.props.isAuthorized("cluster", "", ["get", "create"])) {
-      tabOptions.push({ label: "Create a Cluster", value: "create-cluster" });
+      tabOptions.push({ label: "Create a cluster", value: "create-cluster" });
     }
 
-    tabOptions.push({ label: "Provisioner Status", value: "provisioner" });
+    tabOptions.push({ label: "Provisioner status", value: "provisioner" });
 
     if (!capabilities?.provisioner) {
-      tabOptions = [{ label: "Project Overview", value: "overview" }];
+      tabOptions = [{ label: "Project overview", value: "overview" }];
     }
 
     return (
@@ -255,7 +255,7 @@ const TopRow = styled.div`
 `;
 
 const Description = styled.div`
-  color: #aaaabb;
+  color: #8b949f;
   margin-top: 13px;
   margin-left: 2px;
   font-size: 13px;
@@ -266,7 +266,7 @@ const InfoLabel = styled.div`
   height: 20px;
   display: flex;
   align-items: center;
-  color: #7a838f;
+  color: #8b949f;
   font-size: 13px;
   > i {
     color: #8b949f;
@@ -299,24 +299,23 @@ const Overlay = styled.div`
   display: flex;
   align-items: center;
   justify-content: center;
-  font-size: 24px;
+  font-size: 21px;
   font-weight: 500;
   font-family: "Work Sans", sans-serif;
   color: white;
 `;
 
 const DashboardImage = styled.img`
-  height: 45px;
-  width: 45px;
+  height: 35px;
+  width: 35px;
   border-radius: 5px;
-  box-shadow: 0 2px 5px 4px #00000011;
 `;
 
 const DashboardIcon = styled.div`
   position: relative;
-  height: 45px;
+  height: 35px;
   margin-right: 17px;
-  width: 45px;
+  width: 35px;
   border-radius: 5px;
   display: flex;
   align-items: center;

+ 1 - 6
dashboard/src/main/home/integrations/Integrations.tsx

@@ -126,12 +126,7 @@ const Flex = styled.div`
   }
 `;
 
-const TitleSectionAlt = styled(TitleSection)`
-  margin-left: -42px;
-  width: calc(100% + 42px);
-`;
-
 const StyledIntegrations = styled.div`
-  width: calc(85%);
+  width: 100%;
   min-width: 300px;
 `;

+ 3 - 3
dashboard/src/main/home/launch/Launch.tsx

@@ -19,8 +19,8 @@ import TemplateList from "./TemplateList";
 import { capitalize } from "lodash";
 
 const initialTabOptions = [
-  { label: "New Application", value: "porter" },
-  { label: "Community Add-ons", value: "community" },
+  { label: "New application", value: "porter" },
+  { label: "Community add-ons", value: "community" },
 ];
 
 type TabOption = {
@@ -426,7 +426,7 @@ const Polymer = styled.div`
 `;
 
 const TemplatesWrapper = styled.div`
-  width: calc(85%);
+  width: 100%;
   overflow: visible;
   min-width: 300px;
 `;

+ 2 - 3
dashboard/src/main/home/launch/launch-flow/LaunchFlow.tsx

@@ -363,7 +363,6 @@ const LaunchFlow: React.FC<PropsType> = (props) => {
 
   const renderCurrentPage = () => {
     let { form, currentTab } = props;
-
     if (currentPage === "source" && form?.hasSource) {
       return (
         <SourcePage
@@ -498,8 +497,8 @@ const Polymer = styled.div`
 `;
 
 const StyledLaunchFlow = styled.div`
-  width: calc(90% - 130px);
+  width: calc(100% - 150px);
   min-width: 300px;
   margin-top: ${(props: { disableMarginTop: boolean }) =>
-    props.disableMarginTop ? "inherit" : "calc(40vh - 310px)"};
+    props.disableMarginTop ? "inherit" : "calc(40vh - 270px)"};
 `;

+ 2 - 62
dashboard/src/main/home/navbar/Navbar.tsx

@@ -110,13 +110,6 @@ const I = styled.i`
   margin-right: 7px;
 `;
 
-const PolicySelector = styled(Select)`
-  height: 30px;
-  width: 100px;
-  margin-right: 15px;
-  color: white !important;
-`;
-
 const CloseOverlay = styled.div`
   position: fixed;
   width: 100vw;
@@ -217,20 +210,6 @@ const Dropdown = styled.div`
   }
 `;
 
-const DropdownAlt = styled(Dropdown)`
-  animation: fadeIn 0.3s 0.5s;
-  opacity: 0;
-  animation-fill-mode: forwards;
-  @keyframes fadeIn {
-    from {
-      opacity: 0;
-    }
-    to {
-      opacity: 1;
-    }
-  }
-`;
-
 const StyledNavbar = styled.div`
   width: 100%;
   height: 60px;
@@ -241,21 +220,7 @@ const StyledNavbar = styled.div`
   align-items: center;
   padding-right: 5px;
   justify-content: flex-end;
-  z-index: 1;
-`;
-
-const HelpIcon = styled.div`
-  > a {
-    > i {
-      font-size: 18px;
-      margin-left: 8px;
-      margin-top: 2px;
-      color: #8590ff;
-      :hover {
-        color: #aaaabb;
-      }
-    }
-  }
+  z-index: 0;
 `;
 
 const NavButton = styled.a`
@@ -280,29 +245,4 @@ const NavButton = styled.a`
       props.selected ? "#ffffff" : "#ffffff88"};
     font-size: 24px;
   }
-`;
-
-const FeedbackButton = styled(NavButton)`
-  color: ${(props: { selected?: boolean }) =>
-    props.selected ? "#ffffff" : "#ffffff88"};
-  font-family: "Work Sans", sans-serif;
-  font-size: 14px;
-  margin-right: 20px;
-  :hover {
-    color: #ffffff;
-    > div {
-      > i {
-        color: #ffffff;
-      }
-    }
-  }
-
-  > div {
-    > i {
-      color: ${(props: { selected?: boolean }) =>
-        props.selected ? "#ffffff" : "#ffffff88"};
-      font-size: 26px;
-      margin-right: 6px;
-    }
-  }
-`;
+`;

+ 2 - 2
dashboard/src/main/home/new-project/NewProject.tsx

@@ -117,7 +117,7 @@ export const NewProjectFC = () => {
               <BackButtonImg src={backArrow} />
             </BackButton>
           )}
-          <TitleSection>New Project</TitleSection>
+          <TitleSection>New project</TitleSection>
         </FadeWrapper>
         <FadeWrapper delay="0.7s">
           <Helper>
@@ -147,7 +147,7 @@ export const NewProjectFC = () => {
             />
           </InputWrapper>
           <NewProjectSaveButton
-            text="Create Project"
+            text="Create project"
             disabled={false}
             onClick={createProject}
             status={buttonStatus}

+ 2 - 2
dashboard/src/main/home/onboarding/steps/ConnectRegistry/ConnectRegistry.tsx

@@ -122,9 +122,9 @@ const ConnectRegistry: React.FC<{}> = ({}) => {
           <BackButtonImg src={backArrow} />
         </BackButton>
       )}
-      <TitleSection>Getting Started</TitleSection>
+      <TitleSection>Getting started</TitleSection>
       <Subtitle>
-        Step 2 of 3 - Connect an existing registry (Optional)
+        Step 2 of 3 - Connect an existing registry (optional)
         <DocsHelper
           tooltipText="If you already have an existing image registry, you can connect your existing registry during project creation. If you don't have an image registry or don't know what that means, skip this step. Porter will handle the rest."
           link={

+ 1 - 1
dashboard/src/main/home/onboarding/steps/ConnectSource.tsx

@@ -76,7 +76,7 @@ const ConnectSource: React.FC<{
 
   return (
     <div>
-      <TitleSection>Getting Started</TitleSection>
+      <TitleSection>Getting started</TitleSection>
       <Subtitle>
         Step 1 of 3 - Connect to GitHub
         <DocsHelper

+ 1 - 1
dashboard/src/main/home/onboarding/steps/ProvisionResources/ProvisionResources.tsx

@@ -308,7 +308,7 @@ const ProvisionResources: React.FC<{}> = () => {
           <BackButtonImg src={backArrow} />
         </BackButton>
       )}
-      <TitleSection>Getting Started</TitleSection>
+      <TitleSection>Getting started</TitleSection>
       <Subtitle>
         Step 3 of 3 - Provision resources
         <DocsHelper

+ 1 - 1
dashboard/src/main/home/project-settings/ProjectSettings.tsx

@@ -212,7 +212,7 @@ const Warning = styled.div`
 `;
 
 const StyledProjectSettings = styled.div`
-  width: calc(85%);
+  width: 100%;
   min-width: 300px;
   height: 100vh;
 `;

+ 4 - 1
dashboard/src/main/home/sidebar/ClusterSection.tsx

@@ -8,7 +8,6 @@ import settings from "assets/settings.svg";
 import monojob from "assets/monojob.png";
 import monoweb from "assets/monoweb.png";
 import sliders from "assets/sliders.svg";
-import cluster from "assets/cluster.svg";
 
 import SidebarLink from "./SidebarLink";
 
@@ -35,6 +34,10 @@ export const ClusterSection: React.FC<Props> = ({
     }
   }, [currentCluster]);
 
+  useEffect(() => {
+    setIsExpanded(false);
+  }, [currentProject]);
+
   const renderClusterContent = (cluster: any) => {
     let clusterId = cluster.id;
 

+ 3 - 3
dashboard/src/main/home/sidebar/Sidebar.tsx

@@ -369,7 +369,7 @@ const CollapseButton = styled.div`
 
 const StyledSidebar = styled.section`
   font-family: "Work Sans", sans-serif;
-  width: 235px;
+  width: 240px;
   position: relative;
   padding-top: 20px;
   height: 100vh;
@@ -379,7 +379,7 @@ const StyledSidebar = styled.section`
   animation-fill-mode: forwards;
   @keyframes showSidebar {
     from {
-      margin-left: -235px;
+      margin-left: -240px;
     }
     to {
       margin-left: 0px;
@@ -390,7 +390,7 @@ const StyledSidebar = styled.section`
       margin-left: 0px;
     }
     to {
-      margin-left: -235px;
+      margin-left: -240px;
     }
   }
 `;

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

@@ -1304,6 +1304,7 @@ const upgradeChartValues = baseApi<
   {
     values: string;
     version?: string;
+    latest_revision?: number;
   },
   {
     id: number;

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

@@ -15,7 +15,7 @@ export const infraNames: any = {
 export const integrationList: any = {
   kubernetes: {
     icon:
-      "https://uxwing.com/wp-content/themes/uxwing/download/10-brands-and-social-media/kubernetes.png",
+      "https://upload.wikimedia.org/wikipedia/labs/thumb/b/ba/Kubernetes-icon-color.svg/2110px-Kubernetes-icon-color.svg.png",
     label: "Kubernetes",
     buttonText: "Add a Cluster",
   },

BIN
porter-0.36.0.tgz