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

Merge pull request #246 from porter-dev/beta.3.integration-frontend

Beta.3.integration frontend
jusrhee 5 лет назад
Родитель
Сommit
088fd690fa

+ 1 - 2
dashboard/src/components/ResourceTab.tsx

@@ -176,7 +176,6 @@ const Tooltip = styled.div`
 `;
 
 const ExpandWrapper = styled.div`
-  overflow: hidden;
 `;
 
 const ResourceHeader = styled.div`
@@ -213,7 +212,7 @@ const Metadata = styled.div`
   display: flex;
   align-items: center;
   position: relative;
-  max-width: ${(props: { hasStatus: boolean }) => props.hasStatus ? 'calc(100% - 50px)' : '100%'};
+  max-width: ${(props: { hasStatus: boolean }) => props.hasStatus ? 'calc(100% - 20px)' : '100%'};
 `;
 
 const Status = styled.div`

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

@@ -123,7 +123,7 @@ const ButtonWrapper = styled.div`
 `;
 
 const Button = styled.button`
-  height: 40px;
+  height: 35px;
   font-size: 13px;
   font-weight: 500;
   font-family: 'Work Sans', sans-serif;

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

@@ -114,7 +114,7 @@ const Option = styled.div`
   width: 100%;
   border-top: 1px solid #00000000;
   border-bottom: 1px solid ${(props: { selected: boolean, lastItem: boolean }) => props.lastItem ? '#ffffff00' : '#ffffff15'};
-  height: 35px;
+  height: 37px;
   font-size: 13px;
   padding-top: 9px;
   align-items: center;
@@ -161,11 +161,12 @@ const StyledSelector = styled.div<{ width: string }>`
 
 const MainSelector = styled.div`
   width: ${(props: { expanded: boolean, width: string, height?: string }) => props.width};
-  height: ${(props: { expanded: boolean, width: string, height?: string }) => props.height ? props.height : '30px'};
+  height: ${(props: { expanded: boolean, width: string, height?: string }) => props.height ? props.height : '35px'};
   border: 1px solid #ffffff55;
   font-size: 13px;
   padding: 5px 10px;
   padding-left: 12px;
+  border-radius: 3px;
   display: flex;
   align-items: center;
   justify-content: space-between;

+ 1 - 2
dashboard/src/components/image-selector/ImageSelector.tsx

@@ -62,8 +62,7 @@ export default class ImageSelector extends Component<PropsType, StateType> {
               if (err) {
                 errors.push(1);
               } else {
-                console.log(res.data);
-                res.data.sort((a: any, b: any) => (a.created_at > b.created_at) ? 1 : -1);
+                res.data.sort((a: any, b: any) => (a.name > b.name) ? 1 : -1);
                 // Loop over found image repositories
                 let newImg = res.data.map((img: any) => {
                   if (this.props.selectedImageUrl === img.uri) {

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

@@ -58,7 +58,7 @@ const Required = styled.div`
 `;
 
 const Unit = styled.div`
-  margin-right: 8px;
+  margin-left: 8px;
 `;
 
 const InputWrapper = styled.div`
@@ -77,7 +77,7 @@ const Input = styled.input`
   width: ${(props: { disabled: boolean, width: string }) => props.width ? props.width : '270px'};
   color: ${(props: { disabled: boolean, width: string }) => props.disabled ? '#ffffff44' : 'white'};
   padding: 5px 10px;
-  height: 30px;
+  height: 35px;
 `;
 
 const Label = styled.div`

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

@@ -67,9 +67,10 @@ export default class Home extends Component<PropsType, StateType> {
           return;
         }
         
-        if (res.data.length > 0 && !(currentCluster || includesCompletedInfraSet(res.data))) {
+        if (res.data.length > 0 && (!currentCluster || !includesCompletedInfraSet(res.data))) {
           this.setState({ currentView: 'provisioner', sidebarReady: true, });
         } else {
+          // console.log('getting here', currentCluster)
           this.setState({ currentView: 'dashboard', sidebarReady: true });
         }
       });
@@ -256,7 +257,7 @@ export default class Home extends Component<PropsType, StateType> {
 
   renderContents = () => {
     let { currentView, handleDO } = this.state;
-    if (this.context.currentProject) {
+    if (this.context.currentProject && currentView !== 'new-project') {
       if (currentView === 'cluster-dashboard') {
         return this.renderDashboard();
       } else if (currentView === 'dashboard') {

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

@@ -190,7 +190,7 @@ const Button = styled.div`
   font-family: 'Work Sans', sans-serif;
   border-radius: 20px;
   color: white;
-  height: 30px;
+  height: 35px;
   padding: 0px 8px;
   padding-bottom: 1px;
   margin-right: 10px;
@@ -211,6 +211,7 @@ const Button = styled.div`
     color: white;
     width: 18px;
     height: 18px;
+    font-weight: 600;
     font-size: 12px;
     border-radius: 20px;
     display: flex;

+ 0 - 1
dashboard/src/main/home/cluster-dashboard/chart/ChartList.tsx

@@ -71,7 +71,6 @@ export default class ChartList extends Component<PropsType, StateType> {
 
   setupWebsocket = (kind: string) => {
       let { currentCluster, currentProject } = this.context;
-      console.log(currentCluster)
       let protocol = process.env.NODE_ENV == 'production' ? 'wss' : 'ws';
       let ws = new WebSocket(`${protocol}://${process.env.API_SERVER}/api/projects/${currentProject.id}/k8s/${kind}/status?cluster_id=${currentCluster.id}`);
       ws.onopen = () => {

+ 0 - 1
dashboard/src/main/home/cluster-dashboard/expanded-chart/graph/GraphDisplay.tsx

@@ -430,7 +430,6 @@ export default class GraphDisplay extends Component<PropsType, StateType> {
           node.x = cursorX + scale * (node.x - cursorX);
           node.y = cursorY + scale * (node.y - cursorY);
         } else {
-          console.log('hi')
           node.x = midX + scale * (node.x - midX);
           node.y = midY + scale * (node.y - midY);
         }

+ 44 - 4
dashboard/src/main/home/cluster-dashboard/expanded-chart/status/ControllerTab.tsx

@@ -16,6 +16,7 @@ type PropsType = {
 type StateType = {
   pods: any[],
   raw: any[],
+  showTooltip: boolean,
 };
 
 // Controller tab in log section that displays list of pods on click.
@@ -23,6 +24,7 @@ export default class ControllerTab extends Component<PropsType, StateType> {
   state = {
     pods: [] as any[],
     raw: [] as any[],
+    showTooltip: false,
   }
 
   componentDidMount() {
@@ -108,6 +110,12 @@ export default class ControllerTab extends Component<PropsType, StateType> {
     }
   }
 
+  renderTooltip = (x: string): JSX.Element | undefined => {
+    if (this.state.showTooltip) {
+      return <Tooltip>{x}</Tooltip>;
+    }
+  }
+
   render() {
     let { controller, selectedPod, isLast, selectPod, isFirst } = this.props;
     let [available, total] = this.getAvailability(controller.kind, controller);
@@ -134,9 +142,13 @@ export default class ControllerTab extends Component<PropsType, StateType> {
                   <Circle />
                   <Rail lastTab={i === this.state.raw.length - 1} />
                 </Gutter>
-                <Name>
+                <Name
+                  onMouseOver={() => { this.setState({ showTooltip: true }) }}
+                  onMouseOut={() => { this.setState({ showTooltip: false }) }}
+                >
                   {pod.metadata?.name}
                 </Name>
+                {this.renderTooltip(pod.metadata?.name)}
                 <Status>
                   <StatusColor status={status} />
                   {status}
@@ -204,13 +216,40 @@ const StatusColor = styled.div`
 `;
 
 const Name = styled.div`
-  width: 50%;
+  max-width: calc(100% - 75px);
   overflow: hidden;
-`
+  white-space: nowrap;
+  text-overflow: ellipsis;
+`;
+
+const Tooltip = styled.div`
+  position: absolute;
+  left: 35px;
+  top: 38px;
+  white-space: nowrap;
+  height: 18px;
+  padding: 2px 5px;
+  background: #383842dd;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex: 1;
+  color: white;
+  text-transform: none;
+  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 Tab = styled.div`
   width: 100%;
-  overflow: hidden;
   height: 50px;
   position: relative;
   display: flex;
@@ -221,6 +260,7 @@ const Tab = styled.div`
   font-size: 13px;
   padding: 20px 19px 20px 42px;
   text-shadow: 0px 0px 8px none;
+  overflow: visible;
   cursor: pointer;
   :hover {
     color: white;

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

@@ -97,7 +97,7 @@ export default class Dashboard extends Component<PropsType, StateType> {
             {!currentCluster && (
               <Banner>
                 <i className="material-icons">error_outline</i>
-                This project currently has no clusters connected.
+                This project currently has no clusters conncted.
               </Banner>
             )}
             <ProvisionerSettings 
@@ -126,7 +126,7 @@ const Banner = styled.div`
   border-radius: 5px;
   padding-left: 15px;
   align-items: center;
-  background: #616FEEcc;
+  background: #ffffff11;
   > i {
     margin-right: 10px;
     font-size: 18px;

+ 1 - 2
dashboard/src/main/home/dashboard/StatusPlaceholder.tsx

@@ -77,7 +77,6 @@ const LoadingWrapper = styled.div`
 const Highlight = styled.div`
   color: #8590ff;
   cursor: pointer;
-  text-decoration: underline;
   margin-left: 10px;
   margin-right: 10px;
 `;
@@ -91,7 +90,7 @@ const Banner = styled.div`
   border-radius: 5px;
   padding-left: 15px;
   align-items: center;
-  background: #616FEEcc;
+  background: #ffffff11;
   > i {
     margin-right: 10px;
     font-size: 18px;

+ 80 - 29
dashboard/src/main/home/project-settings/InviteList.tsx

@@ -20,6 +20,8 @@ type StateType = {
   invalidEmail: boolean,
 }
 
+const dummyInvites = [];
+
 export default class InviteList extends Component<PropsType, StateType> {
   state = {
     loading: true,
@@ -128,9 +130,7 @@ export default class InviteList extends Component<PropsType, StateType> {
   renderInvitations = () => {
     let { currentProject } = this.context;
     if (this.state.loading) {
-      return (
-        <Loading />
-      )
+      return <Loading />;
     } else {
       var invContent: any[] = [];
       for (let i = 0; i < this.state.invites.length; i++) {
@@ -150,7 +150,7 @@ export default class InviteList extends Component<PropsType, StateType> {
                 </CopyButton>
               </Td>
             </Tr>
-          )
+          );
         } else if (this.state.invites[i].expired) {
           invContent.push(
             <Tr key={i}>
@@ -179,7 +179,7 @@ export default class InviteList extends Component<PropsType, StateType> {
                 </CopyButton>
               </Td>
             </Tr>
-          )
+          );
         } else {
           invContent.push(
             <Tr key={i}>
@@ -214,10 +214,11 @@ export default class InviteList extends Component<PropsType, StateType> {
       }
       return (
         <>
-          <Subsubtitle>Collaborators</Subsubtitle>
+          <Heading>Invites & Collaborators</Heading>
+          <Helper>Manage pending invites and view collaborators.</Helper>
           {invContent.length > 0
             ? <Table><tbody>{invContent}</tbody></Table>
-            : <BodyText>This project currently has no collaborators.</BodyText>
+            : <Placeholder>This project currently has no invites or collaborators.</Placeholder>
           }
         </>
       )
@@ -228,21 +229,23 @@ export default class InviteList extends Component<PropsType, StateType> {
     return (
       <>
         <Heading isAtTop={true}>Share Project</Heading>
-        <Helper>Generate a project invite for another admin user:</Helper>
-        <CreateInvite>
-          <InputRow
-            value={this.state.email}
-            type='text'
-            setValue={(x: string) => this.setState({ email: x })}
-            width='calc(100%)'
-            placeholder='ex: mrp@getporter.dev'
-          />
+        <Helper>Generate a project invite for another admin user.</Helper>
+        <DarkMatter />
+        <InputRow
+          value={this.state.email}
+          type='text'
+          setValue={(x: string) => this.setState({ email: x })}
+          width='calc(100%)'
+          placeholder='ex: mrp@getporter.dev'
+        />
+        <ButtonWrapper>
           <InviteButton
+            disabled={false}
             onClick={() => this.validateEmail()}
           >
             Create Invite
           </InviteButton>
-        </CreateInvite>
+        </ButtonWrapper>
         {this.state.invalidEmail &&
           <Invalid>
             Invalid Email Address. Try Again.
@@ -256,6 +259,29 @@ export default class InviteList extends Component<PropsType, StateType> {
 
 InviteList.contextType = Context;
 
+const Placeholder = styled.div`
+  width: 100%;
+  height: 200px;
+  display: flex;
+  align-items: center;
+  margin-top: 23px;
+  justify-content: center;
+  background: #ffffff11;
+  border-radius: 5px;
+  color: #ffffff44;
+  font-size: 13px;
+`;
+
+const ButtonWrapper = styled.div`
+  display: flex;
+  align-items: center;
+`;
+
+const DarkMatter = styled.div`
+  width: 100%;
+  margin-top: -10px;
+`;
+
 const Subtitle = styled.div`
   font-size: 18px;
   font-weight: 700;
@@ -290,9 +316,10 @@ const CopyButton = styled.div`
   font-size: 13px;
   margin-left: 12px;
   float: right;
-  width: 128px;
+  width: 120px;
   padding-top: 7px;
   padding-bottom: 6px;
+  cursor: pointer;
   border-radius: 5px;
   border: 1px solid #ffffff20;
   background-color: #ffffff10;
@@ -305,8 +332,32 @@ const CopyButton = styled.div`
   }
 `;
 
-const InviteButton = styled(CopyButton)`
-  margin-bottom: 14px;
+const InviteButton = styled.div<{ disabled: boolean }>`
+  height: 35px;
+  font-size: 13px;
+  font-weight: 500;
+  font-family: 'Work Sans', sans-serif;
+  color: white;
+  display: flex;
+  align-items: center;
+  padding: 0 15px;
+  margin-top: 10px;
+  text-align: left;
+  background: red;
+  float: left;
+  margin-left: 0;
+  justify-content: center;
+  border: 0;
+  border-radius: 5px;
+  background: ${props => !props.disabled ? '#616FEEcc' : '#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%)' : ''};
+  }
+  margin-bottom: 10px;
 `;
 
 const Rower = styled.div`
@@ -317,8 +368,6 @@ const Rower = styled.div`
 `;
 
 const CreateInvite = styled.div`
-  flex-direction: row;
-  align-items: center;
   margin-top: -10px;
 `;
 
@@ -326,10 +375,10 @@ const ShareLink = styled.input`
   outline: none;
   border: none;
   font-size: 13px;
-  background: #ffffff11;
-  border: 1px solid #ffffff55;
-  width: 50%;
+  background: none;
+  width: 60%;
   color: #74a5f7;
+  margin-left: -30px;
   padding: 5px 10px;
   height: 30px;
   text-overflow: ellipsis;
@@ -345,13 +394,15 @@ const Table = styled.table`
   width: 100%;
   border-spacing: 0px;
   border: 1px solid #ffffff55;
+  margin-top: 22px;
   border-radius: 5px;
+  background: #ffffff11;
 `;
 
 const Td = styled.td`
   visibility: ${(props: { isTop: boolean, invis?: boolean }) => props.invis ? 'hidden' : 'visible'};
   white-space: nowrap;
-  padding: 20px 0px;
+  padding: 6px 0px;
   border-top: ${(props: { isTop: boolean, invis?: boolean }) => (props.isTop ? 'none' : '1px solid #ffffff55')};
   &:last-child {
     padding-right: 16px;
@@ -362,9 +413,9 @@ const Tr = styled.tr`
 `;
 
 const MailTd = styled(Td)`
-  padding-left: 16px;
-  max-width: 242px;
-  min-width: 242px;
+  padding: 0 12px;
+  max-width: 300px;
+  min-width: 300px;
   overflow: hidden;
   text-overflow: ellipsis;
   color: #ffffff;

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

@@ -1,10 +1,12 @@
 import React, { Component } from 'react';
 import styled from 'styled-components';
 
+import { Context } from '../../../shared/Context';
+
 import InviteList from './InviteList';
 import TabRegion from '../../../components/TabRegion';
-
-import { Context } from '../../../shared/Context';
+import Heading from '../../../components/values-form/Heading';
+import Helper from '../../../components/values-form/Helper';
 
 type PropsType = {
   setCurrentView: (x: string) => void,
@@ -37,24 +39,20 @@ export default class ProjectSettings extends Component<PropsType, StateType> {
     } else {
       return (
         <>
-          <Subtitle>Other Settings</Subtitle>
-          <Rower>
-            <BodyText>
-              Delete this project: 
-            </BodyText>
-            <DeleteButton
-              onClick={() => {
-                this.context.setCurrentModal('UpdateProjectModal', {
-                  currentProject: this.context.currentProject,
-                  setCurrentView: this.props.setCurrentView,
-                });
-              }}
-            >
-              Delete
-            </DeleteButton>
-          </Rower>
+          <Heading isAtTop={true}>Delete Project</Heading>
+          <Helper>Permanently delete this project. <Warning highlight={true}>This action cannot be undone.</Warning></Helper>
+          <DeleteButton
+            onClick={() => {
+              this.context.setCurrentModal('UpdateProjectModal', {
+                currentProject: this.context.currentProject,
+                setCurrentView: this.props.setCurrentView,
+              });
+            }}
+          >
+            Delete Project
+          </DeleteButton>
         </>
-      )
+      );
     }
   }
 
@@ -78,6 +76,11 @@ export default class ProjectSettings extends Component<PropsType, StateType> {
 
 ProjectSettings.contextType = Context;
 
+const Warning = styled.span`
+  color: ${(props: { highlight: boolean, makeFlush?: boolean }) => props.highlight ? '#f5cb42' : ''};
+  margin-left: 5px;
+`;
+
 const Title = styled.div`
   font-size: 24px;
   font-weight: 600;
@@ -148,15 +151,33 @@ const CopyButton = styled.div`
   }
 `;
 
-const DeleteButton = styled(CopyButton)`
-  background-color: #b91133;
+const DeleteButton = styled.div`
+  height: 35px;
+  font-size: 13px;
+  font-weight: 500;
+  font-family: 'Work Sans', sans-serif;
+  color: white;
+  display: flex;
+  align-items: center;
+  padding: 0 15px;
+  margin-top: 10px;
+  text-align: left;
+  background: red;
+  float: left;
+  margin-left: 0;
+  justify-content: center;
+  border-radius: 5px;
+  box-shadow: 0 2px 5px 0 #00000030;
+  cursor: pointer;
+  user-select: none;
+  :focus { outline: 0 }
+  :hover {
+    filter: brightness(120%);
+  }
+  background: #b91133;
   border: none;
-  width: 88px;
-  margin-left: 20px;
   :hover {
-    background-color: #b91133;
     filter: brightness(120%);
-    border: none;
   }
 `;
 
@@ -164,10 +185,4 @@ const ContentHolder = styled.div`
   min-width: 420px;
   width: 100%;
   margin-bottom: 55px;
-`;
-
-const Rower = styled.div`
-  display: flex;
-  flex-direction: row;
-  align-items: center;
 `;

+ 4 - 3
dashboard/src/main/home/provisioner/AWSFormSection.tsx

@@ -143,15 +143,16 @@ export default class AWSFormSection extends Component<PropsType, StateType> {
             return;
           }
           setProjects(res.data);
-          setCurrentProject(proj);
-          callback && callback();
+          setCurrentProject(proj, () => {
+            callback && callback()
+          });
         });
       }
     });
   }
 
   provisionECR = (callback?: any) => {
-    console.log('Provisioning ECR')
+    console.log('Provisioning ECR');
     let { awsAccessId, awsSecretKey, awsRegion } = this.state;
     let { currentProject } = this.context;
     let { handleError } = this.props;

+ 29 - 26
dashboard/src/main/home/provisioner/GCPFormSection.tsx

@@ -36,32 +36,35 @@ const provisionOptions = [
 ];
 
 const regionOptions = [
-  { value: 'us-east-1', label: 'US East (N. Virginia) us-east-1' },
-  { value: 'us-east-2', label: 'US East (Ohio) us-east-2' },
-  { value: 'us-west-1', label: 'US West (N. California) us-west-1' },
-  { value: 'us-west-2', label: 'US West (Oregon) us-west-2' },
-  { value: 'af-south-1', label: 'Africa (Cape Town) af-south-1' },
-  { value: 'ap-east-1', label: 'Asia Pacific (Hong Kong)ap-east-1' },
-  { value: 'ap-south-1', label: 'Asia Pacific (Mumbai) ap-south-1' },
-  { value: 'ap-northeast-2', label: 'Asia Pacific (Seoul) ap-northeast-2' },
-  { value: 'ap-southeast-1', label: 'Asia Pacific (Singapore) ap-southeast-1' },
-  { value: 'ap-southeast-2', label: 'Asia Pacific (Sydney) ap-southeast-2' },
-  { value: 'ap-northeast-1', label: 'Asia Pacific (Tokyo) ap-northeast-1' },
-  { value: 'ca-central-1', label: 'Canada (Central) ca-central-1' },
-  { value: 'eu-central-1', label: 'Europe (Frankfurt) eu-central-1' },
-  { value: 'eu-west-1', label: 'Europe (Ireland) eu-west-1' },
-  { value: 'eu-west-2', label: 'Europe (London) eu-west-2' },
-  { value: 'eu-south-1', label: 'Europe (Milan) eu-south-1' },
-  { value: 'eu-west-3', label: 'Europe (Paris) eu-west-3' },
-  { value: 'eu-north-1', label: 'Europe (Stockholm) eu-north-1' },
-  { value: 'me-south-1', label: 'Middle East (Bahrain) me-south-1' },
-  { value: 'sa-east-1', label: 'South America (São Paulo) sa-east-1' },
-];
+  { value: 'asia-east1', label: 'asia-east1' },
+  { value: 'asia-east2', label: 'asia-east2' },
+  { value: 'asia-northeast1', label: 'asia-northeast1' },
+  { value: 'asia-northeast2', label: 'asia-northeast2' },
+  { value: 'asia-northeast3', label: 'asia-northeast3' },
+  { value: 'asia-south1', label: 'asia-south1' },
+  { value: 'asia-southeast1', label: 'asia-southeast1' },
+  { value: 'asia-southeast2', label: 'asia-southeast2' },
+  { value: 'australia-southeast1', label: 'australia-southeast1' },
+  { value: 'europe-north1', label: 'europe-north1' },
+  { value: 'europe-west1', label: 'europe-west1' },
+  { value: 'europe-west2', label: 'europe-west2' },
+  { value: 'europe-west3', label: 'europe-west3' },
+  { value: 'europe-west4', label: 'europe-west4' },
+  { value: 'europe-west6', label: 'europe-west6' },
+  { value: 'northamerica-northeast1', label: 'northamerica-northeast1' },
+  { value: 'southamerica-east1', label: 'southamerica-east1' },
+  { value: 'us-central1', label: 'us-central1' },
+  { value: 'us-east1', label: 'us-east1' },
+  { value: 'us-east4', label: 'us-east4' },
+  { value: 'us-west1', label: 'us-west1' },
+  { value: 'us-west2', label: 'us-west2' },
+  { value: 'us-west3', label: 'us-west3' },
+  { value: 'us-west4', label: 'us-west4' },
+]
 
-// TODO: Consolidate across forms w/ HOC
 export default class GCPFormSection extends Component<PropsType, StateType> {
   state = {
-    gcpRegion: 'us-east-1',
+    gcpRegion: 'us-east1',
     gcpProjectId: '',
     gcpKeyData: '',
     selectedInfras: [...provisionOptions],
@@ -243,7 +246,7 @@ export default class GCPFormSection extends Component<PropsType, StateType> {
           <Heading isAtTop={true}>
             GCP Credentials
             <GuideButton 
-              href='https://docs.getporter.dev/docs/getting-started-with-porter-on-aws' 
+              href='https://docs.getporter.dev/docs/getting-started-on-gcp'
               target='_blank'
             >
               <i className="material-icons-outlined">help</i> 
@@ -263,7 +266,7 @@ export default class GCPFormSection extends Component<PropsType, StateType> {
             value={gcpProjectId}
             setValue={(x: string) => this.setState({ gcpProjectId: x })}
             label='🏷️ GCP Project ID'
-            placeholder='ex: AKIAIOSFODNN7EXAMPLE'
+            placeholder='ex: blindfold-ceiling-24601'
             width='100%'
             isRequired={true}
           />
@@ -271,7 +274,7 @@ export default class GCPFormSection extends Component<PropsType, StateType> {
             type='password'
             value={gcpKeyData}
             setValue={(x: string) => this.setState({ gcpKeyData: x })}
-            label='🔒 GCP Key Data'
+            label='🔒 GCP Key Data (JSON)'
             placeholder='○ ○ ○ ○ ○ ○ ○ ○ ○'
             width='100%'
             isRequired={true}

+ 12 - 4
dashboard/src/main/home/provisioner/ProvisionerStatus.tsx

@@ -57,6 +57,7 @@ export default class ProvisionerStatus extends Component<PropsType, StateType> {
 
   componentDidMount() {
     let { currentProject } = this.context;
+    // console.log(currentProject)
     let protocol = process.env.NODE_ENV == 'production' ? 'wss' : 'ws'
 
     // Check if current project is provisioning
@@ -66,8 +67,9 @@ export default class ProvisionerStatus extends Component<PropsType, StateType> {
       if (err) {
         console.log(err);
       } 
+      
       let infras = filterOldInfras(res.data);
-      console.log('filtered infras: ', infras);
+      // console.log('filtered infras: ', infras);
       let error = false;
 
       let maxStep = {} as Record<string, number>
@@ -79,8 +81,6 @@ export default class ProvisionerStatus extends Component<PropsType, StateType> {
         }
       });
 
-      console.log(infras)
-
       // Filter historical infras list for most current instances of each
       let websockets = infras.map((infra: any) => {
         let ws = new WebSocket(`${protocol}://${process.env.API_SERVER}/api/projects/${currentProject.id}/provision/${infra.kind}/${infra.id}/logs`)
@@ -125,7 +125,6 @@ export default class ProvisionerStatus extends Component<PropsType, StateType> {
           let d = JSON.parse(msg["Values"]["data"]);
 
           if (d["kind"] == "error") {
-            console.log(d)
             err = d["log"];
             break;
           }
@@ -210,8 +209,17 @@ export default class ProvisionerStatus extends Component<PropsType, StateType> {
         } else if (res.data) {
           let clusters = res.data;
           if (clusters.length > 0) {
+            // console.log('response :', res.data);
             this.props.setCurrentView('dashboard');
+            alert('setting to dashboard');
+            // console.log('provision end project: ', this.context.currentProject);
+            // console.log('provision end cluster: ', this.context.currentCluster);
             clearInterval(myInterval);
+          } else {
+            // console.log('looped!')
+            // console.log('response :', res.data);
+            // console.log('provision end project: ', this.context.currentProject);
+            // console.log('provision end cluster: ', this.context.currentCluster);
           }
         }
       });

+ 5 - 6
dashboard/src/main/home/templates/expanded-template/TemplateInfo.tsx

@@ -144,8 +144,7 @@ export default class TemplateInfo extends Component<PropsType, StateType> {
 TemplateInfo.contextType = Context;
 
 const Link = styled.a`
-  text-decoration: underline;
-  color: white;
+  color: #8590ff;
   cursor: pointer;
   margin-left: 5px;
 `;
@@ -164,7 +163,7 @@ const Banner = styled.div`
   border-radius: 5px;
   padding-left: 15px;
   align-items: center;
-  background: #616FEEcc;
+  background: #ffffff11;
   > i {
     margin-right: 10px;
     font-size: 18px;
@@ -226,7 +225,7 @@ const Flex = styled.div`
 `;
 
 const Button = styled.div`
-  height: 100%;
+  height: 35px;
   background: ${(props: { isDisabled: boolean }) => (!props.isDisabled ? '#616feecc' : '#aaaabb')};
   :hover {
     background: ${(props: { isDisabled: boolean }) => (!props.isDisabled ? '#505edddd' : '#aaaabb')};
@@ -243,8 +242,8 @@ const Button = styled.div`
   align-items: center;
 
   > img {
-    width: 20px;
-    height: 20px;
+    width: 16px;
+    height: 16px;
     display: flex;
     align-items: center;
     margin-right: 10px;

+ 8 - 4
dashboard/src/shared/Context.tsx

@@ -36,12 +36,16 @@ class ContextProvider extends Component {
       this.setState({ currentError });
     },
     currentCluster: null as ClusterType | null,
-    setCurrentCluster: (currentCluster: ClusterType) => {
-      this.setState({ currentCluster });
+    setCurrentCluster: (currentCluster: ClusterType, callback?: any) => {
+      this.setState({ currentCluster }, () => {
+        callback && callback();
+      });
     },
     currentProject: null as ProjectType | null,
-    setCurrentProject: (currentProject: ProjectType) => {
-      this.setState({ currentProject });
+    setCurrentProject: (currentProject: ProjectType, callback?: any) => {
+      this.setState({ currentProject }, () => {
+        callback && callback();
+      });
     },
     projects: [] as ProjectType[],
     setProjects: (projects: ProjectType[]) => {

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

@@ -328,7 +328,7 @@ const createGCR = baseApi<{
 }, {
   project_id: number,
 }>('POST', pathParams => {
-  return `/api/projects/${pathParams.project_id}/provision/test`;
+  return `/api/projects/${pathParams.project_id}/provision/gcr`;
 });
 
 const createGKE = baseApi<{
@@ -337,7 +337,7 @@ const createGKE = baseApi<{
 }, {
   project_id: number,
 }>('POST', pathParams => {
-  return `/api/projects/${pathParams.project_id}/provision/test`;
+  return `/api/projects/${pathParams.project_id}/provision/gke`;
 });
 
 const createInvite = baseApi<{

+ 11 - 7
dashboard/src/shared/common.tsx

@@ -80,13 +80,13 @@ export const getIgnoreCase = (object: any, key: string) => {
   ];
 }
 
-const infraSets = [
-  ['ecr', 'eks'],
-  ['gcr', 'gke'],
-  ['docr', 'doks']
-];
-
 export const includesCompletedInfraSet = (infras: InfraType[]): boolean => {
+  // TODO: declare globally while avoidiing changes to the array on helper call
+  let infraSets = [
+    ['ecr', 'eks'],
+    ['gcr', 'gke'],
+    ['docr', 'doks']
+  ];
   if (infras.length === 0) {
     return false;
   }
@@ -114,6 +114,11 @@ export const includesCompletedInfraSet = (infras: InfraType[]): boolean => {
 }
 
 export const filterOldInfras = (infras: InfraType[]): InfraType[] => {
+  let infraSets = [
+    ['ecr', 'eks'],
+    ['gcr', 'gke'],
+    ['docr', 'doks']
+  ];
   let newestInstances = {} as any;
   let newestId = -1;
   let whitelistedInfras = [] as string[];
@@ -141,6 +146,5 @@ export const filterOldInfras = (infras: InfraType[]): InfraType[] => {
   let result = newestInfras.filter((x: InfraType) => {
     return whitelistedInfras.includes(x.kind)
   });
-  console.log('filtered infras (helper internal): ', result);
   return result;
 }