Przeglądaj źródła

styled basic app group launch flow + fixed repo selector styling

portersupport 3 lat temu
rodzic
commit
ec9a3d804d

+ 3 - 1
dashboard/src/components/repo-selector/RepoList.tsx

@@ -1,6 +1,6 @@
 import React, { useContext, useEffect, useRef, useState } from "react";
 import React, { useContext, useEffect, useRef, useState } from "react";
 import styled from "styled-components";
 import styled from "styled-components";
-import github from "assets/github.png";
+import github from "assets/github-white.png";
 
 
 import api from "shared/api";
 import api from "shared/api";
 import { ActionConfigType, RepoType } from "shared/types";
 import { ActionConfigType, RepoType } from "shared/types";
@@ -401,6 +401,8 @@ const ProviderSelectorStyles = {
     position: absolute;
     position: absolute;
     background: #37393f;
     background: #37393f;
     border-radius: 3px;
     border-radius: 3px;
+    max-height: 300px;
+    overflow-y: auto;
     width: calc(100% - 4px);
     width: calc(100% - 4px);
     box-shadow: 0 8px 20px 0px #00000088;
     box-shadow: 0 8px 20px 0px #00000088;
   `,
   `,

+ 0 - 99
dashboard/src/main/home/cluster-dashboard/ClusterDashboard.tsx

@@ -347,11 +347,6 @@ const HidableElement = styled.div<{ show: boolean }>`
   display: ${(props) => (props.show ? "unset" : "none")};
   display: ${(props) => (props.show ? "unset" : "none")};
 `;
 `;
 
 
-const Br = styled.div`
-  width: 100%;
-  height: 1px;
-`;
-
 const ControlRow = styled.div`
 const ControlRow = styled.div`
   display: flex;
   display: flex;
   margin-left: auto;
   margin-left: auto;
@@ -361,39 +356,6 @@ const ControlRow = styled.div`
   padding-left: 0px;
   padding-left: 0px;
 `;
 `;
 
 
-const TopRow = styled.div`
-  display: flex;
-  align-items: center;
-`;
-
-const Description = styled.div`
-  color: #aaaabb;
-  margin-top: 13px;
-  margin-left: 2px;
-  font-size: 13px;
-`;
-
-const InfoLabel = styled.div`
-  width: 72px;
-  height: 20px;
-  display: flex;
-  align-items: center;
-  color: #7a838f;
-  font-size: 13px;
-  > i {
-    color: #8b949f;
-    font-size: 18px;
-    margin-right: 5px;
-  }
-`;
-
-const InfoSection = styled.div`
-  margin-top: 20px;
-  font-family: "Work Sans", sans-serif;
-  margin-left: 0px;
-  margin-bottom: 35px;
-`;
-
 const Button = styled.div`
 const Button = styled.div`
   display: flex;
   display: flex;
   flex-direction: row;
   flex-direction: row;
@@ -440,67 +402,6 @@ const Button = styled.div`
   }
   }
 `;
 `;
 
 
-const ButtonAlt = styled(Button)`
-  min-width: 150px;
-  max-width: 150px;
-  background: #7a838fdd;
-
-  :hover {
-    background: #69727eee;
-  }
-`;
-
-const LineBreak = styled.div`
-  width: calc(100% - 0px);
-  height: 2px;
-  background: #ffffff20;
-  margin: 10px 0px 35px;
-`;
-
-const Overlay = styled.div`
-  height: 100%;
-  width: 100%;
-  position: absolute;
-  background: #00000028;
-  top: 0;
-  left: 0;
-  border-radius: 5px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  font-size: 24px;
-  font-weight: 500;
-  font-family: "Work Sans", sans-serif;
-  color: white;
-`;
-
-const DashboardImage = styled.img`
-  height: 45px;
-  width: 45px;
-  border-radius: 5px;
-`;
-
-const DashboardIcon = styled.div`
-  position: relative;
-  height: 45px;
-  min-width: 45px;
-  width: 45px;
-  border-radius: 5px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  background: #676c7c;
-  border: 2px solid #8e94aa;
-
-  > i {
-    font-size: 22px;
-  }
-`;
-
-const Img = styled.img`
-  width: 30px;
-`;
-
 const SortFilterWrapper = styled.div`
 const SortFilterWrapper = styled.div`
   display: flex;
   display: flex;
   justify-content: space-between;
   justify-content: space-between;

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

@@ -30,11 +30,14 @@ const Dashboard = () => {
       <DashboardHeader
       <DashboardHeader
         materialIconClass="material-icons-outlined"
         materialIconClass="material-icons-outlined"
         image={"lan"}
         image={"lan"}
-        title="Preview Environments"
-        description=""
+        title="Stacks"
+        description="Groups of applications deployed from a shared source."
       />
       />
       <ActionRow>
       <ActionRow>
-        <DynamicLink to={"/stacks/launch"}>Create stack</DynamicLink>
+        <Button to={"/stacks/launch"}>
+          <i className="material-icons">add</i>
+          Create Stack
+        </Button>
         <NamespaceSelector
         <NamespaceSelector
           namespace={currentNamespace}
           namespace={currentNamespace}
           setNamespace={handleNamespaceChange}
           setNamespace={handleNamespaceChange}
@@ -47,7 +50,54 @@ const Dashboard = () => {
 
 
 export default Dashboard;
 export default Dashboard;
 
 
+const Button = styled(DynamicLink)`
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  justify-content: space-between;
+  font-size: 13px;
+  cursor: pointer;
+  font-family: "Work Sans", sans-serif;
+  border-radius: 20px;
+  color: white;
+  height: 35px;
+  padding: 0px 8px;
+  min-width: 130px;
+  padding-bottom: 1px;
+  margin-right: 10px;
+  font-weight: 500;
+  padding-right: 15px;
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+  box-shadow: 0 5px 8px 0px #00000010;
+  cursor: ${(props: { disabled?: boolean }) =>
+    props.disabled ? "not-allowed" : "pointer"};
+
+  background: ${(props: { disabled?: boolean }) =>
+    props.disabled ? "#aaaabbee" : "#616FEEcc"};
+  :hover {
+    background: ${(props: { disabled?: boolean }) =>
+      props.disabled ? "" : "#505edddd"};
+  }
+
+  > i {
+    color: white;
+    width: 18px;
+    height: 18px;
+    font-weight: 600;
+    font-size: 12px;
+    border-radius: 20px;
+    display: flex;
+    align-items: center;
+    margin-right: 5px;
+    justify-content: center;
+  }
+`;
+
 const ActionRow = styled.div`
 const ActionRow = styled.div`
   display: flex;
   display: flex;
   justify-content: space-between;
   justify-content: space-between;
+  align-items: center;
+  margin-bottom: 35px;
 `;
 `;

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

@@ -3,8 +3,10 @@ import Loading from "components/Loading";
 import React, { useContext, useEffect, useState } from "react";
 import React, { useContext, useEffect, useState } from "react";
 import api from "shared/api";
 import api from "shared/api";
 import { Context } from "shared/Context";
 import { Context } from "shared/Context";
+import Placeholder from "components/Placeholder";
 import styled from "styled-components";
 import styled from "styled-components";
 import { GetStacksResponse, Stack } from "./types";
 import { GetStacksResponse, Stack } from "./types";
+import { readableDate } from "shared/string_utils";
 
 
 const StackList = ({ namespace }: { namespace: string }) => {
 const StackList = ({ namespace }: { namespace: string }) => {
   const { currentProject, currentCluster, setCurrentError } = useContext(
   const { currentProject, currentCluster, setCurrentError } = useContext(
@@ -51,22 +53,55 @@ const StackList = ({ namespace }: { namespace: string }) => {
 
 
   if (stacks?.length === 0) {
   if (stacks?.length === 0) {
     return (
     return (
-      <div>
+      <Placeholder
+        height="250px"
+      >
+        <div>
         <h3>No stacks found</h3>
         <h3>No stacks found</h3>
-        <p>You can create a stack by clicking the "Create stack" button.</p>
-      </div>
+        <p>You can create a stack by clicking the "Create Stack" button.</p>
+        </div>
+      </Placeholder>
     );
     );
   }
   }
 
 
   return (
   return (
     <>
     <>
       {stacks.map((stack) => (
       {stacks.map((stack) => (
-        <Card to={`/stacks/${namespace}/${stack?.id}`} key={stack?.id}>
-          {stack.name} -{" "}
-          {!stack.latest_revision?.id
-            ? `No revision available for this stack`
-            : `Current Revision: ${stack.latest_revision.id}`}
-        </Card>
+        <StackCard key={stack?.id}>
+          <LinkMask to={`/stacks/${namespace}/${stack?.id}`}  />
+          <DataContainer>
+            <StackName>
+              <StackIcon>
+                <i className="material-icons-outlined">lan</i>
+              </StackIcon>
+              <span>
+                {stack.name}
+              </span>
+            </StackName>
+
+            <Flex>
+              <DeploymentImageContainer>
+                <InfoWrapper>
+                  <LastDeployed>
+                    <Revision>
+                    {!stack.latest_revision?.id
+                    ? `No version found`
+                    : `v${stack.latest_revision.id}`}
+                    </Revision>
+                    <SepDot>•</SepDot>
+                    Last updated {readableDate(stack.updated_at)}
+                  </LastDeployed>
+                </InfoWrapper>
+              </DeploymentImageContainer>
+            </Flex>
+          </DataContainer>
+          <MinFlex>
+            <RowButton>
+              <i className="material-icons">delete</i>
+              Delete
+            </RowButton>
+          </MinFlex>
+        </StackCard>
       ))}
       ))}
     </>
     </>
   );
   );
@@ -74,8 +109,141 @@ const StackList = ({ namespace }: { namespace: string }) => {
 
 
 export default StackList;
 export default StackList;
 
 
-const Card = styled(DynamicLink)`
-  display: block;
+const RowButton = styled.div`
+  white-space: nowrap;
+  font-size: 12px;
+  z-index: 999;
+  padding: 8px 10px;
+  font-weight: 400;
+  height: 32px;
+  margin-right: 5px;
+  margin-left: 10px;
+  border-radius: 5px;
+  color: #ffffff;
+  border: 1px solid #aaaabb;
+  display: flex;
+  align-items: center;
+  background: #ffffff08;
+  cursor: pointer;
+  :hover {
+    background: #ffffff22;
+  }
+
+  > i {
+    font-size: 14px;
+    margin-right: 8px;
+  }
+`;
+
+const Revision = styled.div`
+  color: #aaaabb;
+`;
+
+const StackIcon = styled.div`
+  margin-bottom: -4px;
+
+  > i {
+    font-size: 18px;
+    margin-left: -1px;
+    margin-right: 9px;
+    color: #ffffff66;
+  }
+`;
+
+const StackName = styled.div`
+  font-family: "Work Sans", sans-serif;
+  font-weight: 500;
+  color: #ffffff;
+  display: flex;
+  font-size: 14px;
+  align-items: center;
+  margin-bottom: 10px;
+`;
+
+const SepDot = styled.div`
+  color: #aaaabb66;
+  margin: 0 9px;
+`;
+
+const InfoWrapper = styled.div`
+  display: flex;
+  align-items: center;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  margin-right: 8px;
+`;
+
+const LastDeployed = styled.div`
+  font-size: 13px;
+  margin-top: -1px;
+  display: flex;
+  align-items: center;
+  color: #aaaabb66;
+`;
+
+const DeploymentImageContainer = styled.div`
+  height: 20px;
+  font-size: 13px;
+  position: relative;
+  display: flex;
+  align-items: center;
+  font-weight: 400;
+  justify-content: center;
+  color: #ffffff66;
+  margin-left: 1px;
+`;
+
+const Flex = styled.div`
+  display: flex;
+  align-items: center;
+`;
+
+const MinFlex = styled.div`
+  display: flex;
+  align-items: center;
+`;
+
+const DataContainer = styled.div`
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+  max-width: calc(100% - 100px);
+  overflow: hidden;
+`;
+
+const LinkMask = styled(DynamicLink)`
+  width: 100%;
+  height: 100%;
+  position: absolute;
+  top: 0;
+  left: 0;
+  z-index: 1;
+`;
+
+const StackCard = styled.div`
+  color: #ffffff;
+  display: flex;
+  font-weight: 500;
+  position: relative;
+  background: #2b2e3699;
+  justify-content: space-between;
+  border-radius: 5px;
+  font-size: 13px;
+  height: 75px;
+  padding: 12px;
+  padding-left: 14px;
+  border: 1px solid #ffffff2f;
+
+  animation: fadeIn 0.5s;
+  @keyframes fadeIn {
+    from {
+      opacity: 0;
+    }
+    to {
+      opacity: 1;
+    }
+  }
 `;
 `;
 
 
 const mockApi = () =>
 const mockApi = () =>

+ 92 - 7
dashboard/src/main/home/cluster-dashboard/stacks/launch/NewApp.tsx

@@ -10,6 +10,11 @@ import { Context } from "shared/Context";
 import { useRouting } from "shared/routing";
 import { useRouting } from "shared/routing";
 import { ExpandedPorterTemplate } from "shared/types";
 import { ExpandedPorterTemplate } from "shared/types";
 import { StacksLaunchContext } from "./Store";
 import { StacksLaunchContext } from "./Store";
+import DynamicLink from "components/DynamicLink";
+import styled from "styled-components";
+import Heading from "components/form-components/Heading";
+import TitleSection from "components/TitleSection";
+import { hardcodedIcons } from "shared/hardcodedNameDict";
 
 
 const DEFAULT_STACK_SOURCE_CONFIG_INDEX = 0;
 const DEFAULT_STACK_SOURCE_CONFIG_INDEX = 0;
 
 
@@ -67,7 +72,7 @@ const NewApp = () => {
   }, [params]);
   }, [params]);
 
 
   if (isLoading) {
   if (isLoading) {
-    return <Loading />;
+    return <Wrapper><Loading /></Wrapper>;
   }
   }
 
 
   if (hasError) {
   if (hasError) {
@@ -176,24 +181,104 @@ const NewApp = () => {
   };
   };
 
 
   return (
   return (
-    <div style={{ position: "relative" }}>
-      <Helper>App name</Helper>
+    <StyledLaunchFlow style={{ position: "relative" }}>
+      <TitleSection>
+        <DynamicLink to={`/stacks/launch/overview`}>
+          <BackButton>
+            <i className="material-icons">
+              keyboard_backspace
+            </i>
+          </BackButton>
+        </DynamicLink>
+        <Polymer>
+        <Icon src={hardcodedIcons[template.metadata.name]} />
+        </Polymer>
+        Add {template.metadata.name.charAt(0).toUpperCase() + template.metadata.name.slice(1)} to Stack
+      </TitleSection>
+      <Heading>Application Name <Required>*</Required></Heading>
       <InputRow
       <InputRow
         type="string"
         type="string"
         value={appName}
         value={appName}
         setValue={(val: string) => setAppName(val)}
         setValue={(val: string) => setAppName(val)}
-        width={"300px"}
+        placeholder="ex: perspective-vortex"
+        width="470px"
       />
       />
-      <Helper>App settings</Helper>
+
+      <div style={{ position: "relative" }}>
+      <Heading>Application Settings</Heading>
+      <Helper>Configure settings for this application.</Helper>
       <PorterFormWrapper
       <PorterFormWrapper
         formData={template.form}
         formData={template.form}
         onSubmit={handleSubmit}
         onSubmit={handleSubmit}
         isLaunch
         isLaunch
         saveValuesStatus={saveButtonStatus}
         saveValuesStatus={saveButtonStatus}
-        saveButtonText="Add application"
+        saveButtonText="Add Application"
       />
       />
-    </div>
+      </div>
+    </StyledLaunchFlow>
   );
   );
 };
 };
 
 
 export default NewApp;
 export default NewApp;
+
+const Required = styled.div`
+  margin-left: 8px;
+  color: #fc4976;
+  display: inline-block;
+`;
+
+const Wrapper = styled.div`
+  margin-top: calc(50vh - 150px);
+`;
+
+const Icon = styled.img`
+  width: 40px;
+  margin-right: 14px;
+
+  opacity: 0;
+  animation: floatIn 0.5s 0.2s;
+  animation-fill-mode: forwards;
+  @keyframes floatIn {
+    from {
+      opacity: 0;
+      transform: translateY(10px);
+    }
+    to {
+      opacity: 1;
+      transform: translateY(0px);
+    }
+  }
+`;
+
+const BackButton = styled.div`
+  > i {
+    cursor: pointer;
+    font-size: 24px;
+    color: #969fbbaa;
+    margin-right: 10px;
+    padding: 3px;
+    margin-left: 0px;
+    border-radius: 100px;
+    :hover {
+      background: #ffffff11;
+    }
+  }
+`;
+
+const Polymer = styled.div`
+  margin-bottom: -6px;
+
+  > i {
+    color: #ffffff;
+    font-size: 24px;
+    margin-left: 5px;
+    margin-right: 18px;
+  }
+`;
+
+const StyledLaunchFlow = styled.div`
+  min-width: 300px;
+  margin-top: ${(props: { disableMarginTop?: boolean }) =>
+    props.disableMarginTop ? "inherit" : "calc(50vh - 380px)"};
+  padding-bottom: 150px;
+`;

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

@@ -9,6 +9,11 @@ import { useRouting } from "shared/routing";
 import { CardGrid, SubmitButton } from "./components/styles";
 import { CardGrid, SubmitButton } from "./components/styles";
 import { AppCard } from "./components/AppCard";
 import { AppCard } from "./components/AppCard";
 import { AddResourceButton } from "./components/AddResourceButton";
 import { AddResourceButton } from "./components/AddResourceButton";
+import styled from "styled-components";
+
+import Helper from "components/form-components/Helper";
+import Heading from "components/form-components/Heading";
+import TitleSection from "components/TitleSection";
 
 
 const Overview = () => {
 const Overview = () => {
   const {
   const {
@@ -96,28 +101,55 @@ const Overview = () => {
   }, [namespace, newStack.name]);
   }, [namespace, newStack.name]);
 
 
   return (
   return (
-    <div style={{ position: "relative" }}>
+    <StyledLaunchFlow style={{ position: "relative" }}>
+      <TitleSection handleNavBack={() => window.open("/stacks", "_self")}>
+        <Polymer>
+          <i className="material-icons">lan</i>
+        </Polymer>
+        New Application Stack
+      </TitleSection>
+
+      <Heading>Stack Name</Heading>
+      <Helper>
+        Give this application stack a unique name:
+        <Required>*</Required>
+      </Helper>
       <InputRow
       <InputRow
         type="string"
         type="string"
+        placeholder="ex: perspective-vortices"
+        width="470px"
         value={newStack.name}
         value={newStack.name}
         setValue={(newName: string) => setStackName(newName)}
         setValue={(newName: string) => setStackName(newName)}
       />
       />
 
 
-      <Selector
-        key={"namespace"}
-        refreshOptions={() => {
-          updateNamespaces(currentCluster.id);
-        }}
-        addButton={isAuthorized("namespace", "", ["get", "create"])}
-        activeValue={namespace}
-        setActiveValue={(val) => setStackNamespace(val)}
-        options={namespaceOptions}
-        width="250px"
-        dropdownWidth="335px"
-        closeOverlay={true}
-      />
-
-      <br />
+      <Heading>Destination</Heading>
+      <Helper>
+        Specify the namespace you would like to deploy this stack to.
+      </Helper>
+      <ClusterSection>
+        <NamespaceLabel>
+          <i className="material-icons">view_list</i> Namespace
+        </NamespaceLabel>
+        <Selector
+          key={"namespace"}
+          refreshOptions={() => {
+            updateNamespaces(currentCluster.id);
+          }}
+          addButton={isAuthorized("namespace", "", ["get", "create"])}
+          activeValue={namespace}
+          setActiveValue={(val) => setStackNamespace(val)}
+          options={namespaceOptions}
+          width="250px"
+          dropdownWidth="335px"
+          closeOverlay={true}
+        />
+      </ClusterSection>
+
+      <Heading>Applications</Heading>
+      <Helper>
+        At least one application is required:
+        <Required>*</Required>
+      </Helper>
       <CardGrid>
       <CardGrid>
         {newStack.app_resources.map((app) => (
         {newStack.app_resources.map((app) => (
           <AppCard key={app.name} app={app} />
           <AppCard key={app.name} app={app} />
@@ -134,10 +166,66 @@ const Overview = () => {
         statusPosition="left"
         statusPosition="left"
         status={submitButtonStatus}
         status={submitButtonStatus}
       >
       >
-        Create stack
+        Create Stack
       </SubmitButton>
       </SubmitButton>
-    </div>
+    </StyledLaunchFlow>
   );
   );
 };
 };
 
 
 export default Overview;
 export default Overview;
+
+const NamespaceLabel = styled.div`
+  margin-right: 10px;
+  display: flex;
+  align-items: center;
+  > i {
+    font-size: 16px;
+    margin-right: 6px;
+  }
+`;
+
+const ClusterSection = styled.div`
+  display: flex;
+  align-items: center;
+  color: #ffffff;
+  font-family: "Work Sans", sans-serif;
+  font-size: 14px;
+  margin-top: 20px;
+  font-weight: 500;
+  margin-bottom: 32px;
+
+  > i {
+    font-size: 25px;
+    color: #ffffff44;
+    margin-right: 13px;
+  }
+`;
+
+const Br = styled.div<{ height?: string }>`
+  width: 100%;
+  height: ${props => props.height || "1px"};
+`;
+
+const Required = styled.div`
+  margin-left: 8px;
+  color: #fc4976;
+  display: inline-block;
+`;
+
+const Polymer = styled.div`
+  margin-bottom: -6px;
+
+  > i {
+    color: #ffffff;
+    font-size: 24px;
+    margin-left: 5px;
+    margin-right: 18px;
+  }
+`;
+
+const StyledLaunchFlow = styled.div`
+  min-width: 300px;
+  margin-top: ${(props: { disableMarginTop?: boolean }) =>
+    props.disableMarginTop ? "inherit" : "calc(50vh - 380px)"};
+  padding-bottom: 150px;
+`;

+ 48 - 4
dashboard/src/main/home/cluster-dashboard/stacks/launch/SelectSource.tsx

@@ -4,6 +4,10 @@ import { StacksLaunchContext } from "./Store";
 import { CreateStackBody } from "../types";
 import { CreateStackBody } from "../types";
 import { useRouting } from "shared/routing";
 import { useRouting } from "shared/routing";
 import { SubmitButton } from "./components/styles";
 import { SubmitButton } from "./components/styles";
+import Helper from "components/form-components/Helper";
+import Heading from "components/form-components/Heading";
+import styled from "styled-components";
+import TitleSection from "components/TitleSection";
 
 
 const SelectSource = () => {
 const SelectSource = () => {
   const { addSourceConfig } = useContext(StacksLaunchContext);
   const { addSourceConfig } = useContext(StacksLaunchContext);
@@ -27,7 +31,19 @@ const SelectSource = () => {
   };
   };
 
 
   return (
   return (
-    <div style={{ position: "relative" }}>
+    <StyledLaunchFlow style={{ position: "relative" }}>
+      <TitleSection handleNavBack={() => window.open("/stacks", "_self")}>
+        <Polymer>
+          <i className="material-icons">lan</i>
+        </Polymer>
+        New Application Stack
+      </TitleSection>
+      <Heading>Stack Source</Heading>
+      <Helper>
+        Specify a source to deploy all stack applications from:
+        <Required>*</Required>
+      </Helper>
+      <Br />
       <ImageSelector
       <ImageSelector
         selectedImageUrl={imageUrl}
         selectedImageUrl={imageUrl}
         setSelectedImageUrl={setImageUrl}
         setSelectedImageUrl={setImageUrl}
@@ -35,16 +51,44 @@ const SelectSource = () => {
         setSelectedTag={setImageTag}
         setSelectedTag={setImageTag}
         forceExpanded
         forceExpanded
       />
       />
-
+      <Br height="30px" />
       <SubmitButton
       <SubmitButton
         disabled={!imageUrl || !imageTag}
         disabled={!imageUrl || !imageTag}
         onClick={handleNext}
         onClick={handleNext}
-        text="Next"
+        text="Continue"
         clearPosition
         clearPosition
         makeFlush
         makeFlush
       />
       />
-    </div>
+    </StyledLaunchFlow>
   );
   );
 };
 };
 
 
 export default SelectSource;
 export default SelectSource;
+
+const Br = styled.div<{ height?: string }>`
+  width: 100%;
+  height: ${props => props.height || "1px"};
+`;
+
+const Required = styled.div`
+  margin-left: 8px;
+  color: #fc4976;
+  display: inline-block;
+`;
+
+const Polymer = styled.div`
+  margin-bottom: -6px;
+
+  > i {
+    color: #ffffff;
+    font-size: 24px;
+    margin-left: 5px;
+    margin-right: 18px;
+  }
+`;
+
+const StyledLaunchFlow = styled.div`
+  min-width: 300px;
+  margin-top: ${(props: { disableMarginTop?: boolean }) =>
+    props.disableMarginTop ? "inherit" : "calc(50vh - 380px)"};
+`;

+ 30 - 8
dashboard/src/main/home/cluster-dashboard/stacks/launch/components/AddResourceButton.tsx

@@ -7,6 +7,8 @@ import { TemplateSelector } from "./TemplateSelector";
 import { VersionSelector } from "./VersionSelector";
 import { VersionSelector } from "./VersionSelector";
 import DynamicLink from "components/DynamicLink";
 import DynamicLink from "components/DynamicLink";
 
 
+import styled from "styled-components";
+
 export const AddResourceButton = () => {
 export const AddResourceButton = () => {
   const [templates, setTemplates] = useState<PorterTemplate[]>([]);
   const [templates, setTemplates] = useState<PorterTemplate[]>([]);
   const [currentTemplate, setCurrentTemplate] = useState<PorterTemplate>();
   const [currentTemplate, setCurrentTemplate] = useState<PorterTemplate>();
@@ -50,14 +52,22 @@ export const AddResourceButton = () => {
   useEffect(() => {
   useEffect(() => {
     getTemplates().then((templates) => {
     getTemplates().then((templates) => {
       setTemplates(templates);
       setTemplates(templates);
-      setCurrentTemplate(templates[0]);
-      setCurrentVersion(templates[0].currentVersion);
+      setCurrentTemplate(templates[1]);
+      setCurrentVersion(templates[1].currentVersion);
     });
     });
   }, []);
   }, []);
 
 
   return (
   return (
     <AddResourceButtonStyles.Wrapper>
     <AddResourceButtonStyles.Wrapper>
       <AddResourceButtonStyles.Flex>
       <AddResourceButtonStyles.Flex>
+        <LinkMask
+          to={`/stacks/launch/new-app/${currentTemplate?.name}/${currentVersion}`}
+        >
+          
+        </LinkMask>
+        <Icon>
+          <i className="material-icons">add</i>
+        </Icon>
         Add a new{" "}
         Add a new{" "}
         <TemplateSelector
         <TemplateSelector
           options={templates}
           options={templates}
@@ -73,12 +83,24 @@ export const AddResourceButton = () => {
           onChange={setCurrentVersion}
           onChange={setCurrentVersion}
         />
         />
       </AddResourceButtonStyles.Flex>
       </AddResourceButtonStyles.Flex>
-
-      <DynamicLink
-        to={`/stacks/launch/new-app/${currentTemplate?.name}/${currentVersion}`}
-      >
-        Create
-      </DynamicLink>
     </AddResourceButtonStyles.Wrapper>
     </AddResourceButtonStyles.Wrapper>
   );
   );
 };
 };
+
+const LinkMask = styled(DynamicLink)`
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+`;
+
+const Icon = styled.div`
+  margin-bottom: -3px;
+  > i {
+    margin-right: 20px;
+    margin-left: 9px;
+    font-size: 20px;
+    color: #aaaabb;
+  }
+`;

+ 35 - 5
dashboard/src/main/home/cluster-dashboard/stacks/launch/components/AppCard.tsx

@@ -1,6 +1,9 @@
 import React, { useContext } from "react";
 import React, { useContext } from "react";
 import { StacksLaunchContext, StacksLaunchContextType } from "../Store";
 import { StacksLaunchContext, StacksLaunchContextType } from "../Store";
 import { ButtonWithIcon, Card } from "./styles";
 import { ButtonWithIcon, Card } from "./styles";
+import { hardcodedIcons } from "shared/hardcodedNameDict";
+
+import styled from "styled-components";
 
 
 const DeleteButton = ButtonWithIcon;
 const DeleteButton = ButtonWithIcon;
 
 
@@ -16,11 +19,38 @@ export const AppCard = ({
   };
   };
 
 
   return (
   return (
-    <Card>
+    <UnclickableCard>
+      <Flex>
+      <Icon src={hardcodedIcons[app.template_name]} />
       {app.name}
       {app.name}
-      <DeleteButton onClick={handleDelete}>
-        <i className="material-icons-outlined">delete</i>
-      </DeleteButton>
-    </Card>
+      </Flex>
+      <Delete onClick={handleDelete}>
+        <i className="material-icons-outlined">close</i>
+      </Delete>
+    </UnclickableCard>
   );
   );
 };
 };
+
+const UnclickableCard = styled(Card)`
+  cursor: default;
+  :hover {
+    border: 1px solid #ffffff0f;
+  }
+`;
+
+const Delete = styled(DeleteButton)`
+  margin-right: 5px;
+`;
+
+const Flex = styled.div`
+  display: flex;
+  align-items: center;
+  font-size: 14px;
+  font-weight: 500;
+`;
+
+const Icon = styled.img`
+  height: 30px;
+  margin-right: 15px;
+  margin-left: 5px;
+`;

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

@@ -44,6 +44,12 @@ export const SubmitButton = styled(SaveButton)`
 export const AddResourceButtonStyles = {
 export const AddResourceButtonStyles = {
   Wrapper: styled(Card)`
   Wrapper: styled(Card)`
     align-items: center;
     align-items: center;
+    position: relative;
+    font-size: 14px;
+    height: 50px;
+    :hover {
+      background: #ffffff19;
+    }
   `,
   `,
   Text: styled.span`
   Text: styled.span`
     font-size: 20px;
     font-size: 20px;

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

@@ -496,8 +496,6 @@ const Polymer = styled.div`
 `;
 `;
 
 
 const StyledLaunchFlow = styled.div`
 const StyledLaunchFlow = styled.div`
-  width: calc(90% - 130px);
-  min-width: 300px;
   margin-top: ${(props: { disableMarginTop: boolean }) =>
   margin-top: ${(props: { disableMarginTop: boolean }) =>
-    props.disableMarginTop ? "inherit" : "calc(50vh - 380px)"};
+    props.disableMarginTop ? "inherit" : "calc(40vh - 310px)"};
 `;
 `;