jusrhee 5 лет назад
Родитель
Сommit
03d5e3dcbc

+ 0 - 1
dashboard/src/main/Main.tsx

@@ -72,7 +72,6 @@ export default class Main extends Component<PropsType, StateType> {
       "integrations",
       "new-project",
       "cluster-dashboard",
-      "provisioner",
       "project-settings",
     ];
 

+ 356 - 293
dashboard/src/main/home/Home.tsx

@@ -1,46 +1,46 @@
-import React, { Component } from 'react';
-import posthog from 'posthog-js';
-import styled from 'styled-components';
-
-import { Context } from 'shared/Context';
-import api from 'shared/api';
-import { ClusterType, ProjectType } from 'shared/types';
-
-import Sidebar from './sidebar/Sidebar';
-import Dashboard from './dashboard/Dashboard';
-import ClusterDashboard from './cluster-dashboard/ClusterDashboard';
-import Loading from 'components/Loading';
-import Templates from './templates/Templates';
+import React, { Component } from "react";
+import posthog from "posthog-js";
+import styled from "styled-components";
+
+import { Context } from "shared/Context";
+import api from "shared/api";
+import { ClusterType, ProjectType } from "shared/types";
+
+import Sidebar from "./sidebar/Sidebar";
+import Dashboard from "./dashboard/Dashboard";
+import ClusterDashboard from "./cluster-dashboard/ClusterDashboard";
+import Loading from "components/Loading";
+import Templates from "./templates/Templates";
 import Integrations from "./integrations/Integrations";
-import UpdateClusterModal from './modals/UpdateClusterModal';
-import ClusterInstructionsModal from './modals/ClusterInstructionsModal';
-import IntegrationsModal from './modals/IntegrationsModal';
-import IntegrationsInstructionsModal from './modals/IntegrationsInstructionsModal';
-import NewProject from './new-project/NewProject';
-import Navbar from './navbar/Navbar';
-import ProjectSettings from './project-settings/ProjectSettings';
-import ConfirmOverlay from 'components/ConfirmOverlay';
-import Modal from './modals/Modal';
-import * as FullStory from '@fullstory/browser';
-import { Redirect, RouteComponentProps, withRouter } from 'react-router';
-import {PorterUrls} from 'shared/urls';
+import UpdateClusterModal from "./modals/UpdateClusterModal";
+import ClusterInstructionsModal from "./modals/ClusterInstructionsModal";
+import IntegrationsModal from "./modals/IntegrationsModal";
+import IntegrationsInstructionsModal from "./modals/IntegrationsInstructionsModal";
+import NewProject from "./new-project/NewProject";
+import Navbar from "./navbar/Navbar";
+import ProjectSettings from "./project-settings/ProjectSettings";
+import ConfirmOverlay from "components/ConfirmOverlay";
+import Modal from "./modals/Modal";
+import * as FullStory from "@fullstory/browser";
+import { Redirect, RouteComponentProps, withRouter } from "react-router";
+import { PorterUrls } from "shared/urls";
 
 type PropsType = RouteComponentProps & {
-  logOut: () => void,
-  currentProject: ProjectType,
-  currentCluster: ClusterType,
-  currentRoute: PorterUrls,
+  logOut: () => void;
+  currentProject: ProjectType;
+  currentCluster: ClusterType;
+  currentRoute: PorterUrls;
 };
 
 type StateType = {
-  forceSidebar: boolean,
-  showWelcome: boolean,
-  handleDO: boolean, // Trigger DO infra calls after oauth flow if needed
-  forceRefreshClusters: boolean, // For updating ClusterSection from modal on deletion
-  templateNamespace: string,
+  forceSidebar: boolean;
+  showWelcome: boolean;
+  handleDO: boolean; // Trigger DO infra calls after oauth flow if needed
+  ghRedirect: boolean;
+  forceRefreshClusters: boolean; // For updating ClusterSection from modal on deletion
+
   // Track last project id for refreshing clusters on project change
-  prevProjectId: number | null,
-  ghRedirect: boolean,
+  prevProjectId: number | null;
 };
 
 // TODO: Handle cluster connected but with some failed infras (no successful set)
@@ -48,181 +48,202 @@ class Home extends Component<PropsType, StateType> {
   state = {
     forceSidebar: true,
     showWelcome: false,
-    ghRedirect: false,
     prevProjectId: null as number | null,
     forceRefreshClusters: false,
-    templateNamespace: '',
     sidebarReady: false,
     handleDO: false,
-  }
+    ghRedirect: false,
+  };
 
   // TODO: Refactor and prevent flash + multiple reload
   initializeView = () => {
     let { currentProject } = this.props;
-    
+
     if (!currentProject) return;
 
     if (this.state.ghRedirect) {
       this.props.history.push("integrations");
       this.setState({ ghRedirect: false });
     } else {
-      this.props.history.push("dashboard");
+      if (this.props.currentRoute !== "dashboard")
+        this.props.history.push("dashboard");
     }
-  }
+  };
 
   getProjects = (id?: number) => {
     let { user, setProjects } = this.context;
     let { currentProject } = this.props;
-    api.getProjects('<token>', {}, { id: user.userId }, (err: any, res: any) => {
-      if (err) {
-        console.log(err);
-      } else if (res.data) {
-        if (res.data.length === 0) {
-          this.props.history.push("new-project");
-        } else if (res.data.length > 0 && !currentProject) {
-          setProjects(res.data);
-
-          let foundProject = null;
-          if (id) {
-            res.data.forEach((project: ProjectType, i: number) => {
-              if (project.id === id) {
-                foundProject = project;
-              } 
-            });
-            this.context.setCurrentProject(foundProject);
-          }
-
-          if (!foundProject) {
-            res.data.forEach((project: ProjectType, i: number) => {
-              if (project.id.toString() === localStorage.getItem('currentProject')) {
-                foundProject = project;
-              }
-            })
-            this.context.setCurrentProject(foundProject ? foundProject : res.data[0]);
-            this.initializeView();
+    api.getProjects(
+      "<token>",
+      {},
+      { id: user.userId },
+      (err: any, res: any) => {
+        if (err) {
+          console.log(err);
+        } else if (res.data) {
+          if (res.data.length === 0) {
+            this.props.history.push("new-project");
+          } else if (res.data.length > 0 && !currentProject) {
+            setProjects(res.data);
+
+            let foundProject = null;
+            if (id) {
+              res.data.forEach((project: ProjectType, i: number) => {
+                if (project.id === id) {
+                  foundProject = project;
+                }
+              });
+              this.context.setCurrentProject(foundProject);
+            }
+
+            if (!foundProject) {
+              res.data.forEach((project: ProjectType, i: number) => {
+                if (
+                  project.id.toString() ===
+                  localStorage.getItem("currentProject")
+                ) {
+                  foundProject = project;
+                }
+              });
+              this.context.setCurrentProject(
+                foundProject ? foundProject : res.data[0]
+              );
+              this.initializeView();
+            }
           }
         }
       }
-    });
-  }
+    );
+  };
 
   provisionDOCR = (integrationId: number, tier: string, callback?: any) => {
-    console.log('Provisioning DOCR...');
-    api.createDOCR('<token>', {
-      do_integration_id: integrationId,
-      docr_name: this.props.currentProject.name,
-      docr_subscription_tier: tier,
-    }, { 
-      project_id: this.props.currentProject.id
-    }, (err: any, res: any) => {
-      if (err) {
-        console.log(err);
-        return;
+    console.log("Provisioning DOCR...");
+    api.createDOCR(
+      "<token>",
+      {
+        do_integration_id: integrationId,
+        docr_name: this.props.currentProject.name,
+        docr_subscription_tier: tier,
+      },
+      {
+        project_id: this.props.currentProject.id,
+      },
+      (err: any, res: any) => {
+        if (err) {
+          console.log(err);
+          return;
+        }
+        callback && callback();
       }
-      callback && callback();
-    });
-  }
+    );
+  };
 
   provisionDOKS = (integrationId: number, region: string) => {
-    console.log('Provisioning DOKS...');
-    api.createDOKS('<token>', {
-      do_integration_id: integrationId,
-      doks_name: this.props.currentProject.name,
-      do_region: region,
-    }, { 
-      project_id: this.props.currentProject.id
-    }, (err: any, res: any) => {
-      if (err) {
-        console.log(err);
-        return;
+    console.log("Provisioning DOKS...");
+    api.createDOKS(
+      "<token>",
+      {
+        do_integration_id: integrationId,
+        doks_name: this.props.currentProject.name,
+        do_region: region,
+      },
+      {
+        project_id: this.props.currentProject.id,
+      },
+      (err: any, res: any) => {
+        if (err) {
+          console.log(err);
+          return;
+        }
+        this.props.history.push("dashboard?tab=provisioner");
       }
-      <Redirect to="provisioner"></Redirect>
-    });
-  }
+    );
+  };
 
   checkDO = () => {
     let { currentProject } = this.props;
     if (this.state.handleDO && currentProject?.id) {
-      api.getOAuthIds('<token>', {}, { 
-        project_id: currentProject.id
-      }, (err: any, res: any) => {
-        if (err) {
-          console.log(err);
-          return;
-        }
-        let tgtIntegration = res.data.find((integration: any) => {
-          return integration.client === 'do'
-        });
-        let queryString = window.location.search;
-        let urlParams = new URLSearchParams(queryString);
-        let tier = urlParams.get('tier');
-        let region = urlParams.get('region');
-        let infras = urlParams.getAll('infras');
-        if (infras.length === 2) {
-          this.provisionDOCR(tgtIntegration.id, tier, () => {
-            this.provisionDOKS(tgtIntegration.id, region);
-          });
-        } else if (infras[0] === 'docr') {
-          this.provisionDOCR(tgtIntegration.id, tier, () => {
-            <Redirect to="provisioner"></Redirect>
+      api.getOAuthIds(
+        "<token>",
+        {},
+        {
+          project_id: currentProject.id,
+        },
+        (err: any, res: any) => {
+          if (err) {
+            console.log(err);
+            return;
+          }
+          let tgtIntegration = res.data.find((integration: any) => {
+            return integration.client === "do";
           });
-        } else {
-          this.provisionDOKS(tgtIntegration.id, region);
+          let queryString = window.location.search;
+          let urlParams = new URLSearchParams(queryString);
+          let tier = urlParams.get("tier");
+          let region = urlParams.get("region");
+          let infras = urlParams.getAll("infras");
+          if (infras.length === 2) {
+            this.provisionDOCR(tgtIntegration.id, tier, () => {
+              this.provisionDOKS(tgtIntegration.id, region);
+            });
+          } else if (infras[0] === "docr") {
+            this.provisionDOCR(tgtIntegration.id, tier, () => {
+              this.props.history.push("dashboard?tab=provisioner");
+            });
+          } else {
+            this.provisionDOKS(tgtIntegration.id, region);
+          }
         }
-      });
+      );
       this.setState({ handleDO: false });
     }
-  }
+  };
 
   componentDidMount() {
     let { user } = this.context;
-    FullStory.identify(user.email)
+    FullStory.identify(user.email);
 
     // Handle redirect from DO
     let queryString = window.location.search;
     let urlParams = new URLSearchParams(queryString);
 
-    let err = urlParams.get('error');
+    let err = urlParams.get("error");
     if (err) {
       this.context.setCurrentError(err);
     }
 
-    let provision = urlParams.get('provision');
+    let provision = urlParams.get("provision");
     let defaultProjectId = null;
-    if (provision === 'do') {
-      defaultProjectId = parseInt(urlParams.get('project_id'));
+    if (provision === "do") {
+      defaultProjectId = parseInt(urlParams.get("project_id"));
       this.setState({ handleDO: true });
       this.checkDO();
     }
 
     // initialize posthog on non-localhosts. Gracefully fail when env vars are not set.
-    this.setState({ ghRedirect: urlParams.get('gh_oauth') !== null });
-    urlParams.delete('gh_oauth');
-    
-    window.location.href.indexOf('localhost') === -1 && posthog.init(process.env.POSTHOG_API_KEY || 'placeholder', {
-      api_host: process.env.POSTHOG_HOST || 'placeholder',
-      loaded: function(posthog: any) { 
-        posthog.identify(user.userId) 
-        posthog.people.set({ email: user.email })
-      }
-    })
+    this.setState({ ghRedirect: urlParams.get("gh_oauth") !== null });
+    urlParams.delete("gh_oauth");
+
+    window.location.href.indexOf("localhost") === -1 &&
+      posthog.init(process.env.POSTHOG_API_KEY || "placeholder", {
+        api_host: process.env.POSTHOG_HOST || "placeholder",
+        loaded: function (posthog: any) {
+          posthog.identify(user.userId);
+          posthog.people.set({ email: user.email });
+        },
+      });
 
     this.getProjects(defaultProjectId);
   }
 
-  handleTemplateDeploy = (namespace: string) => {
-    this.setState({ templateNamespace: namespace });
-    // Add routing
-  }
-
   // TODO: Need to handle the following cases. Do a deep rearchitecture (Prov -> Dashboard?) if need be:
   // 1. Make sure clicking cluster in drawer shows cluster-dashboard
   // 2. Make sure switching projects shows appropriate initial view (dashboard || provisioner)
   // 3. Make sure initializing from URL (DO oauth) displays the appropriate initial view
   componentDidUpdate(prevProps: PropsType) {
     if (
-      prevProps.currentProject !== this.props.currentProject
-      || (!prevProps.currentCluster && this.props.currentCluster)
+      prevProps.currentProject !== this.props.currentProject ||
+      (!prevProps.currentCluster && this.props.currentCluster)
     ) {
       if (this.state.handleDO) {
         this.checkDO();
@@ -239,219 +260,254 @@ class Home extends Component<PropsType, StateType> {
       return (
         <DashboardWrapper>
           <Placeholder>
-            <Bold>Porter - Getting Started</Bold><br /><br />
-            1. Navigate to <A onClick={() => setCurrentModal('ClusterConfigModal')}>+ Add a Cluster</A> and provide a kubeconfig. *<br /><br />
-            2. Choose which contexts you would like to use from the <A onClick={() => {
-              setCurrentModal('ClusterConfigModal', { currentTab: 'select' });
-            }}>Select Clusters</A> tab.<br /><br />
-            3. For additional information, please refer to our <A>docs</A>.<br /><br /><br />
-
-            * Make sure all fields are explicitly declared (e.g., certs and keys).
+            <Bold>Porter - Getting Started</Bold>
+            <br />
+            <br />
+            1. Navigate to{" "}
+            <A onClick={() => setCurrentModal("ClusterConfigModal")}>
+              + Add a Cluster
+            </A>{" "}
+            and provide a kubeconfig. *<br />
+            <br />
+            2. Choose which contexts you would like to use from the{" "}
+            <A
+              onClick={() => {
+                setCurrentModal("ClusterConfigModal", { currentTab: "select" });
+              }}
+            >
+              Select Clusters
+            </A>{" "}
+            tab.
+            <br />
+            <br />
+            3. For additional information, please refer to our <A>docs</A>.
+            <br />
+            <br />
+            <br />* Make sure all fields are explicitly declared (e.g., certs
+            and keys).
           </Placeholder>
         </DashboardWrapper>
       );
     } else if (!currentCluster) {
-      return <Loading />
+      return <Loading />;
     }
 
     return (
       <DashboardWrapper>
         <ClusterDashboard
           currentCluster={currentCluster}
-          namespace={this.state.templateNamespace}
-          resetNamespace={() => this.setState({ templateNamespace: '' })}
           setSidebar={(x: boolean) => this.setState({ forceSidebar: x })}
           // setCurrentView={(x: string) => this.setState({ currentView: x })}
         />
       </DashboardWrapper>
     );
-  }
+  };
 
   renderContents = () => {
     let currentView = this.props.currentRoute;
-    if (this.context.currentProject && currentView !== 'new-project') {
-      if (currentView === 'cluster-dashboard') {
+    if (this.context.currentProject && currentView !== "new-project") {
+      if (currentView === "cluster-dashboard") {
         return this.renderDashboard();
-      } else if (currentView === 'dashboard') {
+      } else if (currentView === "dashboard") {
         return (
           <DashboardWrapper>
-            <Dashboard 
-              projectId={this.context.currentProject?.id}
-            />
+            <Dashboard projectId={this.context.currentProject?.id} />
           </DashboardWrapper>
         );
-      } else if (currentView === 'integrations') {
+      } else if (currentView === "integrations") {
         return <Integrations />;
-      } else if (currentView === 'project-settings') {
-        return (
-          <ProjectSettings />
-        )
+      } else if (currentView === "project-settings") {
+        return <ProjectSettings />;
       }
 
-      return (
-        <Templates />
-      );
-    } else if (currentView === 'new-project') {
-      return (
-        <NewProject/>
-      );
+      return <Templates />;
+    } else if (currentView === "new-project") {
+      return <NewProject />;
     }
-  }
+  };
 
   renderSidebar = () => {
     if (this.context.projects.length > 0) {
-
-      // Force sidebar closed on first provision
-      if (this.props.currentRoute === 'provisioner' && this.state.forceSidebar) {
-        this.setState({ forceSidebar: false });
-      } else {
-        return (
-          <Sidebar
-            key="sidebar"
-            forceSidebar={this.state.forceSidebar}
-            setWelcome={(x: boolean) => this.setState({ showWelcome: x })}
-            currentView={this.props.currentRoute}
-            forceRefreshClusters={this.state.forceRefreshClusters}
-            setRefreshClusters={(x: boolean) => this.setState({ forceRefreshClusters: x })}
-          />
-        );
-      }
+      return (
+        <Sidebar
+          key="sidebar"
+          forceSidebar={this.state.forceSidebar}
+          setWelcome={(x: boolean) => this.setState({ showWelcome: x })}
+          currentView={this.props.currentRoute}
+          forceRefreshClusters={this.state.forceRefreshClusters}
+          setRefreshClusters={(x: boolean) =>
+            this.setState({ forceRefreshClusters: x })
+          }
+        />
+      );
     }
-  }
+  };
 
   projectOverlayCall = () => {
     let { user, setProjects } = this.context;
-    api.getProjects('<token>', {}, { id: user.userId }, (err: any, res: any) => {
-      if (err) {
-        console.log(err)
-      } else if (res.data) {
-        setProjects(res.data);
-        if (res.data.length > 0) {
-          this.context.setCurrentProject(res.data[0]);
-          // Redirect to dashboard
-        } else {
-          this.context.setCurrentProject(null);
-          this.props.history.push("new-project");
+    api.getProjects(
+      "<token>",
+      {},
+      { id: user.userId },
+      (err: any, res: any) => {
+        if (err) {
+          console.log(err);
+        } else if (res.data) {
+          setProjects(res.data);
+          if (res.data.length > 0) {
+            this.context.setCurrentProject(res.data[0]);
+          } else {
+            this.context.setCurrentProject(null);
+            this.props.history.push("new-project");
+          }
+          this.context.setCurrentModal(null, null);
         }
-        this.context.setCurrentModal(null, null);
       }
-    });
-  }
+    );
+  };
 
   handleDelete = () => {
     let { setCurrentModal, currentProject } = this.context;
-    localStorage.removeItem(currentProject.id + '-cluster');
-    api.deleteProject('<token>', {}, { id: currentProject.id }, (err: any, res: any) => {
-      if (err) {
-        console.log(err)
-      } else {
-        this.projectOverlayCall();
+    localStorage.removeItem(currentProject.id + "-cluster");
+    api.deleteProject(
+      "<token>",
+      {},
+      { id: currentProject.id },
+      (err: any, res: any) => {
+        if (err) {
+          console.log(err);
+        } else {
+          this.projectOverlayCall();
+        }
       }
-    });
+    );
 
     // Loop through and delete infra of all clusters we've provisioned
-    api.getClusters('<token>', {}, { id: currentProject.id }, (err: any, res: any) => {
+    api.getClusters(
+      "<token>",
+      {},
+      { id: currentProject.id },
+      (err: any, res: any) => {
+        if (err) {
+          console.log(err);
+          return;
+        }
 
-      if (err) { 
-        console.log(err); 
-        return; 
-      }
-      
-      for (var i = 0; i < res.data.length; i++) {
-        let cluster = res.data[i];
-        if (!cluster.infra_id) continue;
-
-        // Handle destroying infra we've provisioned
-        switch (cluster.service) {
-
-          case "eks":
-            api.destroyEKS('<token>', { eks_name: cluster.name }, { 
-              project_id: currentProject.id,
-              infra_id: cluster.infra_id,
-            }, (err: any, res: any) => {
-              if (err) {
-                console.log(err)
-              } else {
-                console.log('destroyed provisioned infra:', cluster.infra_id);
-              }
-            });
-            break;
-
-          case 'gke':
-            api.destroyGKE('<token>', { gke_name: cluster.name }, { 
-              project_id: currentProject.id,
-              infra_id: cluster.infra_id,
-            }, (err: any, res: any) => {
-              if (err) {
-                console.log(err)
-              } else {
-                console.log('destroyed provisioned infra.');
-              }
-            });
-            break;
-
-          case 'doks':
-            api.destroyDOKS('<token>', { doks_name: cluster.name }, { 
-              project_id: currentProject.id,
-              infra_id: cluster.infra_id,
-            }, (err: any, res: any) => {
-              if (err) {
-                console.log(err)
-              } else {
-                console.log('destroyed provisioned infra.');
-              }
-            });
-            break;
+        for (var i = 0; i < res.data.length; i++) {
+          let cluster = res.data[i];
+          if (!cluster.infra_id) continue;
+
+          // Handle destroying infra we've provisioned
+          switch (cluster.service) {
+            case "eks":
+              api.destroyEKS(
+                "<token>",
+                { eks_name: cluster.name },
+                {
+                  project_id: currentProject.id,
+                  infra_id: cluster.infra_id,
+                },
+                (err: any, res: any) => {
+                  if (err) {
+                    console.log(err);
+                  } else {
+                    console.log(
+                      "destroyed provisioned infra:",
+                      cluster.infra_id
+                    );
+                  }
+                }
+              );
+              break;
+
+            case "gke":
+              api.destroyGKE(
+                "<token>",
+                { gke_name: cluster.name },
+                {
+                  project_id: currentProject.id,
+                  infra_id: cluster.infra_id,
+                },
+                (err: any, res: any) => {
+                  if (err) {
+                    console.log(err);
+                  } else {
+                    console.log("destroyed provisioned infra.");
+                  }
+                }
+              );
+              break;
+
+            case "doks":
+              api.destroyDOKS(
+                "<token>",
+                { doks_name: cluster.name },
+                {
+                  project_id: currentProject.id,
+                  infra_id: cluster.infra_id,
+                },
+                (err: any, res: any) => {
+                  if (err) {
+                    console.log(err);
+                  } else {
+                    console.log("destroyed provisioned infra.");
+                  }
+                }
+              );
+              break;
+          }
         }
       }
-    });
+    );
     setCurrentModal(null, null);
     this.props.history.push("dashboard");
-  }
+  };
 
   render() {
     let { currentModal, setCurrentModal, currentProject } = this.context;
 
     return (
       <StyledHome>
-        {currentModal === 'ClusterInstructionsModal' &&
+        {currentModal === "ClusterInstructionsModal" && (
           <Modal
             onRequestClose={() => setCurrentModal(null, null)}
-            width='760px'
-            height='650px'
+            width="760px"
+            height="650px"
           >
             <ClusterInstructionsModal />
           </Modal>
-        }
-        {currentModal === 'UpdateClusterModal' &&
+        )}
+        {currentModal === "UpdateClusterModal" && (
           <Modal
             onRequestClose={() => setCurrentModal(null, null)}
-            width='565px'
-            height='275px'
+            width="565px"
+            height="275px"
           >
-            <UpdateClusterModal 
-              setRefreshClusters={(x: boolean) => this.setState({ forceRefreshClusters: x })} 
+            <UpdateClusterModal
+              setRefreshClusters={(x: boolean) =>
+                this.setState({ forceRefreshClusters: x })
+              }
             />
           </Modal>
-        }
-        {currentModal === 'IntegrationsModal' &&
+        )}
+        {currentModal === "IntegrationsModal" && (
           <Modal
             onRequestClose={() => setCurrentModal(null, null)}
-            width='760px'
-            height='725px'
+            width="760px"
+            height="725px"
           >
             <IntegrationsModal />
           </Modal>
-        }
-        {currentModal === 'IntegrationsInstructionsModal' &&
+        )}
+        {currentModal === "IntegrationsInstructionsModal" && (
           <Modal
             onRequestClose={() => setCurrentModal(null, null)}
-            width='760px'
-            height='650px'
+            width="760px"
+            height="650px"
           >
             <IntegrationsInstructionsModal />
           </Modal>
-        }
+        )}
 
         {this.renderSidebar()}
 
@@ -464,8 +520,12 @@ class Home extends Component<PropsType, StateType> {
         </ViewWrapper>
 
         <ConfirmOverlay
-          show={currentModal === 'UpdateProjectModal'}
-          message={(currentProject) ? `Are you sure you want to delete ${currentProject.name}?` : ''}
+          show={currentModal === "UpdateProjectModal"}
+          message={
+            currentProject
+              ? `Are you sure you want to delete ${currentProject.name}?`
+              : ""
+          }
           onYes={this.handleDelete}
           onNo={() => setCurrentModal(null, null)}
         />
@@ -500,7 +560,8 @@ const DashboardWrapper = styled.div`
 const A = styled.a`
   color: #ffffff;
   text-decoration: underline;
-  cursor: ${(props: { disabled?: boolean }) => props.disabled ? 'not-allowed' : 'pointer'};
+  cursor: ${(props: { disabled?: boolean }) =>
+    props.disabled ? "not-allowed" : "pointer"};
 `;
 
 const Placeholder = styled.div`
@@ -530,10 +591,12 @@ const StyledHome = styled.div`
 
   @keyframes floatInModal {
     from {
-      opacity: 0; transform: translateY(30px);
+      opacity: 0;
+      transform: translateY(30px);
     }
     to {
-      opacity: 1; transform: translateY(0px);
+      opacity: 1;
+      transform: translateY(0px);
     }
   }
-`;
+`;

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

@@ -14,9 +14,7 @@ import { Redirect, RouteComponentProps, withRouter } from 'react-router';
 
 type PropsType = RouteComponentProps & {
   currentCluster: ClusterType,
-  namespace: string,
-  setSidebar: (x: boolean) => void
-  resetNamespace: () => void,
+  setSidebar: (x: boolean) => void,
 };
 
 type StateType = {
@@ -32,14 +30,6 @@ class ClusterDashboard extends Component<PropsType, StateType> {
     currentChart: null as (ChartType | null)
   }
 
-  componentDidMount() {
-    if (this.props.namespace) {
-      this.setState({ namespace: this.props.namespace }, () => {
-        this.props.resetNamespace();
-      })
-    }
-  }
-
   componentDidUpdate(prevProps: PropsType) {
     localStorage.setItem("SortType", this.state.sortType);
     // Reset namespace filter and close expanded chart on cluster change

+ 79 - 74
dashboard/src/main/home/dashboard/Dashboard.tsx

@@ -1,52 +1,60 @@
-import { render } from '@testing-library/react';
-import React, { Component } from 'react';
-import styled from 'styled-components';
+import { render } from "@testing-library/react";
+import React, { Component } from "react";
+import styled from "styled-components";
 
-import gradient from 'assets/gradient.jpg';
-import { Context } from 'shared/Context';
-import { InfraType } from 'shared/types';
-import api from 'shared/api';
+import gradient from "assets/gradient.jpg";
+import { Context } from "shared/Context";
+import { InfraType } from "shared/types";
+import api from "shared/api";
 
-import ProvisionerSettings from '../provisioner/ProvisionerSettings';
-import ClusterPlaceholderContainer from './ClusterPlaceholderContainer';
-import { Redirect, RouteComponentProps, withRouter } from 'react-router';
-import TabRegion from 'components/TabRegion';
-import Provisioner from '../provisioner/Provisioner';
+import ProvisionerSettings from "../provisioner/ProvisionerSettings";
+import ClusterPlaceholderContainer from "./ClusterPlaceholderContainer";
+import { Redirect, RouteComponentProps, withRouter } from "react-router";
+import TabRegion from "components/TabRegion";
+import Provisioner from "../provisioner/Provisioner";
 
 type PropsType = RouteComponentProps & {
-  projectId: number | null,
+  projectId: number | null;
 };
 
+const tabOptions = [
+  { label: "Project Overview", value: "overview" },
+  { label: "Provisioner Status", value: "provisioner" },
+];
+// TODO: rethink this typing, should be coupled with tabOptions
+type TabType = "overview" | "provisioner"
+
 type StateType = {
-  infras: InfraType[],
-  currentTab: string,
+  infras: InfraType[];
+  currentTab: TabType;
 };
 
-const tabOptions = [
-  { label: 'Project Overview', value: 'overview' },
-  { label: 'Provisioner Status', value: 'provisioner' },
-];
 
 class Dashboard extends Component<PropsType, StateType> {
   state = {
     infras: [] as InfraType[],
-    currentTab: 'main',
-  }
+    currentTab: "overview" as TabType,
+  };
 
   refreshInfras = () => {
     if (this.props.projectId) {
-      api.getInfra('<token>', {}, { 
-        project_id: this.props.projectId,
-      }, (err: any, res: any) => {
-        if (err) {
-          console.log(err);
-          return;
-        } 
-        this.setState({ infras: res.data });
-      });
+      api.getInfra(
+        "<token>",
+        {},
+        {
+          project_id: this.props.projectId,
+        },
+        (err: any, res: any) => {
+          if (err) {
+            console.log(err);
+            return;
+          }
+          this.setState({ infras: res.data });
+        }
+      );
     }
-  }
-  
+  };
+
   componentDidMount() {
     this.refreshInfras();
   }
@@ -59,36 +67,36 @@ class Dashboard extends Component<PropsType, StateType> {
 
   onShowProjectSettings = () => {
     this.props.history.push("project-settings");
-  }
+  };
 
   renderTabContents = () => {
-    if (this.state.currentTab === 'provisioner') {
-      return (
-        <Provisioner
-        />
-      );
+    const currentTab = new URLSearchParams(this.props.location.search).get("tab")
+    if (
+      currentTab && currentTab !== this.state.currentTab
+    ) {
+      this.setState({ currentTab: currentTab as TabType });
+    }
+
+    if (this.state.currentTab === "provisioner") {
+      return <Provisioner />;
     } else {
       return (
         <>
-          {!this.context.currentCluster 
-            ? (
-              <>
-                <Banner>
-                  <i className="material-icons">error_outline</i>
-                  This project currently has no clusters conncted.
-                  </Banner>
-                <ProvisionerSettings 
-                  infras={this.state.infras}
-                />
-              </>
-            ) : (
-              <ClusterPlaceholderContainer/>
-            )
-          }
+          {!this.context.currentCluster ? (
+            <>
+              <Banner>
+                <i className="material-icons">error_outline</i>
+                This project currently has no clusters conncted.
+              </Banner>
+              <ProvisionerSettings infras={this.state.infras} />
+            </>
+          ) : (
+            <ClusterPlaceholderContainer />
+          )}
         </>
       );
     }
-  }
+  };
 
   render() {
     let { currentProject, currentCluster } = this.context;
@@ -99,30 +107,27 @@ class Dashboard extends Component<PropsType, StateType> {
         {currentProject && (
           <DashboardWrapper>
             <TitleSection>
-            <DashboardIcon>
-              <DashboardImage src={gradient} />
-              <Overlay>
-                {currentProject && currentProject.name[0].toUpperCase()}
-              </Overlay>
-            </DashboardIcon>
+              <DashboardIcon>
+                <DashboardImage src={gradient} />
+                <Overlay>
+                  {currentProject && currentProject.name[0].toUpperCase()}
+                </Overlay>
+              </DashboardIcon>
               <Title>{currentProject && currentProject.name}</Title>
               {this.context.currentProject.roles.filter((obj: any) => {
                 return obj.user_id === this.context.user.userId;
-              })[0].kind === 'admin' &&
-                <i
-                  className="material-icons"
-                  onClick={onShowProjectSettings}
-                >
+              })[0].kind === "admin" && (
+                <i className="material-icons" onClick={onShowProjectSettings}>
                   more_vert
                 </i>
-              }
+              )}
             </TitleSection>
 
             <InfoSection>
               <TopRow>
                 <InfoLabel>
                   <i className="material-icons">info</i> Info
-              </InfoLabel>
+                </InfoLabel>
               </TopRow>
               <Description>
                 Project overview for {currentProject && currentProject.name}.
@@ -131,7 +136,7 @@ class Dashboard extends Component<PropsType, StateType> {
 
             <TabRegion
               currentTab={this.state.currentTab}
-              setCurrentTab={(x: string) => this.setState({ currentTab: x })}
+              setCurrentTab={(x: TabType) => this.props.history.push(`dashboard?tab=${x}`)}
               options={tabOptions}
             >
               {this.renderTabContents()}
@@ -184,10 +189,10 @@ const InfoLabel = styled.div`
   height: 20px;
   display: flex;
   align-items: center;
-  color: #7A838F;
+  color: #7a838f;
   font-size: 13px;
   > i {
-    color: #8B949F;
+    color: #8b949f;
     font-size: 18px;
     margin-right: 5px;
   }
@@ -195,7 +200,7 @@ const InfoLabel = styled.div`
 
 const InfoSection = styled.div`
   margin-top: 20px;
-  font-family: 'Work Sans', sans-serif;
+  font-family: "Work Sans", sans-serif;
   margin-left: 0px;
   margin-bottom: 30px;
 `;
@@ -220,7 +225,7 @@ const Overlay = styled.div`
   justify-content: center;
   font-size: 24px;
   font-weight: 500;
-  font-family: 'Work Sans', sans-serif;
+  font-family: "Work Sans", sans-serif;
   color: white;
 `;
 
@@ -247,7 +252,7 @@ const DashboardIcon = styled.div`
 const Title = styled.div`
   font-size: 20px;
   font-weight: 500;
-  font-family: 'Work Sans', sans-serif;
+  font-family: "Work Sans", sans-serif;
   margin-left: 18px;
   color: #ffffff;
   white-space: nowrap;
@@ -276,4 +281,4 @@ const TitleSection = styled.div`
     }
     margin-bottom: -3px;
   }
-`;
+`;

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

@@ -210,7 +210,7 @@ class AWSFormSection extends Component<PropsType, StateType> {
           handleError();
           return;
         }
-        this.props.history.push("provisioner");
+        this.props.history.push("dashboard?tab=provisioner");
       })
     })
   }
@@ -226,7 +226,7 @@ class AWSFormSection extends Component<PropsType, StateType> {
         this.provisionECR(this.provisionEKS);
       } else if (selectedInfras[0].value === 'ecr') {
         // Case: project exists, only provision ECR
-        this.provisionECR(() => this.props.history.push("provisioner"));
+        this.provisionECR(() => this.props.history.push("dashboard?tab=provisioner"));
       } else {
         // Case: project exists, only provision EKS
         this.provisionEKS();
@@ -238,7 +238,7 @@ class AWSFormSection extends Component<PropsType, StateType> {
       } else if (selectedInfras[0].value === 'ecr') {
         // Case: project DNE, only provision ECR
         this.createProject(() => this.provisionECR(() => {
-          this.props.history.push("provisioner");
+          this.props.history.push("dashboard?tab=provisioner");
         }));
       } else {
         // Case: project DNE, only provision EKS

+ 2 - 2
dashboard/src/main/home/provisioner/GCPFormSection.tsx

@@ -184,7 +184,7 @@ class GCPFormSection extends Component<PropsType, StateType> {
         handleError();
         return;
       }
-      this.props.history.push("provisioner");
+      this.props.history.push("dashboard?tab=provisioner");
     })
   }
 
@@ -207,7 +207,7 @@ class GCPFormSection extends Component<PropsType, StateType> {
           this.provisionGCR(id, () => this.provisionGKE(id));
         } else if (selectedInfras[0].value === 'gcr') {
           // Case: project exists, only provision GCR
-          this.provisionGCR(id, () => this.props.history.push("provisioner"));
+          this.provisionGCR(id, () => this.props.history.push("dashboard?tab=provisioner"));
         } else {
           // Case: project exists, only provision GKE
           this.provisionGKE(id);

+ 4 - 4
dashboard/src/main/home/provisioner/Provisioner.tsx

@@ -3,10 +3,9 @@ import styled from 'styled-components';
 
 import api from 'shared/api';
 import { Context } from 'shared/Context';
-import ansiparse from 'shared/ansiparser'
 import loading from 'assets/loading.gif';
 import warning from 'assets/warning.png';
-import { InfraType } from 'shared/types';
+import { InfraType, ProjectType } from 'shared/types';
 import Loading from 'components/Loading';
 
 import Helper from 'components/values-form/Helper';
@@ -27,6 +26,7 @@ type StateType = {
   infras: InfraType[],
   loading: boolean,
   selectedInfra: InfraType,
+  currentProject: ProjectType,
 };
 
 class Provisioner extends Component<PropsType, StateType> {
@@ -40,6 +40,7 @@ class Provisioner extends Component<PropsType, StateType> {
     infras: [] as InfraType[],
     selectedInfra: null as InfraType,
     loading: true,
+    currentProject: this.context.currentProject,
   }
 
   selectInfra = (infra: InfraType) => {
@@ -47,7 +48,7 @@ class Provisioner extends Component<PropsType, StateType> {
   }
 
   componentDidMount() {
-    let { currentProject } = this.context;
+    let { currentProject } = this.state;
 
     api.getInfra('<token>', {}, { 
       project_id: currentProject.id 
@@ -64,7 +65,6 @@ class Provisioner extends Component<PropsType, StateType> {
   }
 
   render() {
-    console.log(this.state.infras)
     if (this.state.loading) {
       return (
         <StyledProvisioner> 

+ 1 - 2
dashboard/src/main/home/provisioner/ProvisionerLogs.tsx

@@ -35,7 +35,6 @@ class ProvisionerLogs extends Component<PropsType, StateType> {
   parentRef = React.createRef<HTMLDivElement>()
 
   scrollToBottom = () => {
-    console.log(this.parentRef.current)
     this.parentRef.current.lastElementChild.scrollIntoView({ behavior: "auto" })
   }
 
@@ -85,7 +84,7 @@ class ProvisionerLogs extends Component<PropsType, StateType> {
       let event = JSON.parse(evt.data);
       let validEvents = [] as any[];
       let err = null;
-      
+      console.log(event)
       for (var i = 0; i < event.length; i++) {
         let msg = event[i];
         if (msg["Values"] && msg["Values"]["data"] && this.isJSON(msg["Values"]["data"])) { 

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

@@ -4,7 +4,6 @@ export const PorterUrls = [
   "integrations",
   "new-project",
   "cluster-dashboard",
-  "provisioner",
   "project-settings",
 ];