Parcourir la source

Merge branch 'belanger/por-83-usage-enforcement' of github.com:porter-dev/porter into belanger/por-83-usage-enforcement

jnfrati il y a 4 ans
Parent
commit
8d5b7bfa42
40 fichiers modifiés avec 400 ajouts et 782 suppressions
  1. 57 0
      api/server/handlers/user/welcome_webhook.go
  2. 25 0
      api/server/router/user.go
  3. 7 0
      api/types/user.go
  4. 6 3
      dashboard/src/components/porter-form/field-components/KeyValueArray.tsx
  5. 2 2
      dashboard/src/components/repo-selector/ContentsList.tsx
  6. 1 3
      dashboard/src/main/auth/Login.tsx
  7. 1 3
      dashboard/src/main/auth/ResetPasswordInit.tsx
  8. 1 3
      dashboard/src/main/auth/VerifyEmail.tsx
  9. 11 5
      dashboard/src/main/home/Home.tsx
  10. 1 1
      dashboard/src/main/home/WelcomeForm.tsx
  11. 19 14
      dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedChart.tsx
  12. 4 8
      dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedJobChart.tsx
  13. 5 4
      dashboard/src/main/home/cluster-dashboard/expanded-chart/NotificationSettingsSection.tsx
  14. 7 3
      dashboard/src/main/home/cluster-dashboard/expanded-chart/RevisionSection.tsx
  15. 11 12
      dashboard/src/main/home/cluster-dashboard/expanded-chart/SettingsSection.tsx
  16. 1 2
      dashboard/src/main/home/cluster-dashboard/expanded-chart/ValuesYaml.tsx
  17. 21 26
      dashboard/src/main/home/cluster-dashboard/expanded-chart/events/EventsTab.tsx
  18. 2 4
      dashboard/src/main/home/cluster-dashboard/expanded-chart/jobs/JobList.tsx
  19. 2 4
      dashboard/src/main/home/cluster-dashboard/expanded-chart/jobs/JobResource.tsx
  20. 1 2
      dashboard/src/main/home/cluster-dashboard/expanded-chart/metrics/MetricsSection.tsx
  21. 1 2
      dashboard/src/main/home/cluster-dashboard/expanded-chart/status/ControllerTab.tsx
  22. 1 2
      dashboard/src/main/home/cluster-dashboard/expanded-chart/status/Logs.tsx
  23. 1 2
      dashboard/src/main/home/cluster-dashboard/expanded-chart/status/StatusSection.tsx
  24. 2 2
      dashboard/src/main/home/launch/Launch.tsx
  25. 1 1
      dashboard/src/main/home/launch/launch-flow/SourcePage.tsx
  26. 0 72
      dashboard/src/main/home/modals/AccountSettingsModal.tsx
  27. 5 75
      dashboard/src/main/home/modals/ClusterInstructionsModal.tsx
  28. 3 61
      dashboard/src/main/home/modals/DeleteNamespaceModal.tsx
  29. 5 52
      dashboard/src/main/home/modals/EditInviteOrCollaboratorModal.tsx
  30. 3 109
      dashboard/src/main/home/modals/IntegrationsInstructionsModal.tsx
  31. 7 63
      dashboard/src/main/home/modals/IntegrationsModal.tsx
  32. 44 3
      dashboard/src/main/home/modals/Modal.tsx
  33. 3 61
      dashboard/src/main/home/modals/NamespaceModal.tsx
  34. 16 71
      dashboard/src/main/home/modals/UpdateClusterModal.tsx
  35. 89 89
      dashboard/src/main/home/modals/UsageWarningModal.tsx
  36. 23 3
      dashboard/src/main/home/project-settings/BillingPage.tsx
  37. 1 0
      dashboard/src/main/home/provisioner/ProvisionerSettings.tsx
  38. 7 10
      dashboard/src/shared/api.tsx
  39. 2 4
      dashboard/src/shared/baseApi.tsx
  40. 1 1
      docs/guides/using-env-groups.md

+ 57 - 0
api/server/handlers/user/welcome_webhook.go

@@ -0,0 +1,57 @@
+package user
+
+import (
+	"net/http"
+	"net/url"
+
+	"github.com/gorilla/schema"
+	"github.com/porter-dev/porter/api/server/handlers"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/apierrors"
+	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/types"
+)
+
+type UserWelcomeHandler struct {
+	handlers.PorterHandlerReadWriter
+}
+
+func NewUserWelcomeHandler(
+	config *config.Config,
+	decoderValidator shared.RequestDecoderValidator,
+	writer shared.ResultWriter,
+) *UserWelcomeHandler {
+	return &UserWelcomeHandler{
+		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
+	}
+}
+
+func (u *UserWelcomeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	reqVals := &types.WelcomeWebhookRequest{}
+
+	if ok := u.DecodeAndValidate(w, r, reqVals); !ok {
+		return
+	}
+
+	req, err := http.NewRequest("GET", u.Config().ServerConf.WelcomeFormWebhook, nil)
+
+	if err != nil {
+		return
+	}
+
+	encoder := schema.NewEncoder()
+	dst := make(url.Values)
+
+	if err := encoder.Encode(reqVals, dst); err != nil {
+		u.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	req.URL.RawQuery = dst.Encode()
+	_, err = http.Get(req.URL.String())
+
+	if err != nil {
+		u.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+}

+ 25 - 0
api/server/router/user.go

@@ -48,6 +48,31 @@ func getUserRoutes(
 ) []*Route {
 ) []*Route {
 	routes := make([]*Route, 0)
 	routes := make([]*Route, 0)
 
 
+	// POST /api/welcome -> user.NewUserWelcomeHandler
+	welcomeEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbCreate,
+			Method: types.HTTPVerbPost,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: "/welcome",
+			},
+			Scopes: []types.PermissionScope{types.UserScope},
+		},
+	)
+
+	welcomeHandler := user.NewUserWelcomeHandler(
+		config,
+		factory.GetDecoderValidator(),
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &Route{
+		Endpoint: welcomeEndpoint,
+		Handler:  welcomeHandler,
+		Router:   r,
+	})
+
 	// GET /api/cli/login -> user.user.NewCLILoginHandler
 	// GET /api/cli/login -> user.user.NewCLILoginHandler
 	cliLoginUserEndpoint := factory.NewAPIEndpoint(
 	cliLoginUserEndpoint := factory.NewAPIEndpoint(
 		&types.APIRequestMetadata{
 		&types.APIRequestMetadata{

+ 7 - 0
api/types/user.go

@@ -60,3 +60,10 @@ type FinalizeResetUserPasswordRequest struct {
 }
 }
 
 
 type ListUserProjectsResponse []*Project
 type ListUserProjectsResponse []*Project
+
+type WelcomeWebhookRequest struct {
+	Email     string `json:"email" schema:"email"`
+	IsCompany bool   `json:"isCompany" schema:"isCompany"`
+	Company   string `json:"company" schema:"company"`
+	Role      string `json:"role" schema:"role"`
+}

+ 6 - 3
dashboard/src/components/porter-form/field-components/KeyValueArray.tsx

@@ -171,11 +171,14 @@ const KeyValueArray: React.FC<Props> = (props) => {
                 const prevValues = prev.values.reduce((acc, currentValue) => {
                 const prevValues = prev.values.reduce((acc, currentValue) => {
                   acc[currentValue.key] = currentValue.value;
                   acc[currentValue.key] = currentValue.value;
                   return acc;
                   return acc;
-                }, {} as Record<string, string>)
+                }, {} as Record<string, string>);
 
 
                 // Deconstruct the two records/objects inside one to merge their values (this will override the old duped vars too)
                 // Deconstruct the two records/objects inside one to merge their values (this will override the old duped vars too)
                 // and convert the new object back to an array usable for the component
                 // and convert the new object back to an array usable for the component
-                const newValues = Object.entries({...prevValues, ...values})?.map(([k, v]) => {
+                const newValues = Object.entries({
+                  ...prevValues,
+                  ...values,
+                })?.map(([k, v]) => {
                   return {
                   return {
                     key: k,
                     key: k,
                     value: v,
                     value: v,
@@ -183,7 +186,7 @@ const KeyValueArray: React.FC<Props> = (props) => {
                 });
                 });
 
 
                 return {
                 return {
-                  values: [...newValues]
+                  values: [...newValues],
                 };
                 };
               });
               });
             }}
             }}

+ 2 - 2
dashboard/src/components/repo-selector/ContentsList.tsx

@@ -425,7 +425,7 @@ export default class ContentsList extends Component<PropsType, StateType> {
             <p>
             <p>
               <b>{this.state.autoBuildpack.name}</b> buildpack was{" "}
               <b>{this.state.autoBuildpack.name}</b> buildpack was{" "}
               <a
               <a
-                href="https://docs.getporter.dev/docs/auto-deploy-requirements#auto-build-with-cloud-native-buildpacks"
+                href="https://docs.porter.run/docs/auto-deploy-requirements#auto-build-with-cloud-native-buildpacks"
                 target="_blank"
                 target="_blank"
               >
               >
                 detected automatically
                 detected automatically
@@ -440,7 +440,7 @@ export default class ContentsList extends Component<PropsType, StateType> {
           <FlexWrapper>
           <FlexWrapper>
             <UseButton onClick={this.handleContinue}>Continue</UseButton>
             <UseButton onClick={this.handleContinue}>Continue</UseButton>
             <StatusWrapper
             <StatusWrapper
-              href="https://docs.getporter.dev/docs/auto-deploy-requirements#auto-build-with-cloud-native-buildpacks"
+              href="https://docs.porter.run/docs/auto-deploy-requirements#auto-build-with-cloud-native-buildpacks"
               target="_blank"
               target="_blank"
             >
             >
               <i className="material-icons">help_outline</i>
               <i className="material-icons">help_outline</i>

+ 1 - 3
dashboard/src/main/auth/Login.tsx

@@ -92,9 +92,7 @@ export default class Login extends Component<PropsType, StateType> {
             authenticate();
             authenticate();
           }
           }
         })
         })
-        .catch((err) =>
-          this.context.setCurrentError(err.response.data.error)
-        );
+        .catch((err) => this.context.setCurrentError(err.response.data.error));
     }
     }
   };
   };
 
 

+ 1 - 3
dashboard/src/main/auth/ResetPasswordInit.tsx

@@ -64,9 +64,7 @@ export default class ResetPasswordInit extends Component<PropsType, StateType> {
         .then((res) => {
         .then((res) => {
           this.setState({ submitted: true });
           this.setState({ submitted: true });
         })
         })
-        .catch((err) =>
-          this.context.setCurrentError(err.response.data.error)
-        );
+        .catch((err) => this.context.setCurrentError(err.response.data.error));
     }
     }
   };
   };
 
 

+ 1 - 3
dashboard/src/main/auth/VerifyEmail.tsx

@@ -24,9 +24,7 @@ export default class VerifyEmail extends Component<PropsType, StateType> {
       .then((res) => {
       .then((res) => {
         this.setState({ submitted: true });
         this.setState({ submitted: true });
       })
       })
-      .catch((err) =>
-        this.context.setCurrentError(err.response.data.error)
-      );
+      .catch((err) => this.context.setCurrentError(err.response.data.error));
   };
   };
 
 
   render() {
   render() {

+ 11 - 5
dashboard/src/main/home/Home.tsx

@@ -407,9 +407,7 @@ class Home extends Component<PropsType, StateType> {
             <Icon src={discordLogo} />
             <Icon src={discordLogo} />
             Join Our Discord
             Join Our Discord
           </DiscordButton>
           </DiscordButton>
-          {(this.context?.capabilities?.version === "production" ||
-            this.context?.capabilities?.version === "staging") &&
-            this.state.showWelcomeForm &&
+          {this.state.showWelcomeForm &&
             localStorage.getItem("welcomed") != "true" && (
             localStorage.getItem("welcomed") != "true" && (
               <>
               <>
                 <WelcomeForm
                 <WelcomeForm
@@ -500,6 +498,7 @@ class Home extends Component<PropsType, StateType> {
             onRequestClose={() => setCurrentModal(null, null)}
             onRequestClose={() => setCurrentModal(null, null)}
             width="760px"
             width="760px"
             height="650px"
             height="650px"
+            title="Connecting to an Existing Cluster"
           >
           >
             <ClusterInstructionsModal />
             <ClusterInstructionsModal />
           </Modal>
           </Modal>
@@ -512,6 +511,7 @@ class Home extends Component<PropsType, StateType> {
               onRequestClose={() => setCurrentModal(null, null)}
               onRequestClose={() => setCurrentModal(null, null)}
               width="565px"
               width="565px"
               height="275px"
               height="275px"
+              title="Cluster Settings"
             >
             >
               <UpdateClusterModal
               <UpdateClusterModal
                 setRefreshClusters={(x: boolean) =>
                 setRefreshClusters={(x: boolean) =>
@@ -524,7 +524,8 @@ class Home extends Component<PropsType, StateType> {
           <Modal
           <Modal
             onRequestClose={() => setCurrentModal(null, null)}
             onRequestClose={() => setCurrentModal(null, null)}
             width="760px"
             width="760px"
-            height="725px"
+            height="380px"
+            title="Add a New Integration"
           >
           >
             <IntegrationsModal />
             <IntegrationsModal />
           </Modal>
           </Modal>
@@ -534,6 +535,7 @@ class Home extends Component<PropsType, StateType> {
             onRequestClose={() => setCurrentModal(null, null)}
             onRequestClose={() => setCurrentModal(null, null)}
             width="760px"
             width="760px"
             height="650px"
             height="650px"
+            title="Connecting to an Image Registry"
           >
           >
             <IntegrationsInstructionsModal />
             <IntegrationsInstructionsModal />
           </Modal>
           </Modal>
@@ -544,6 +546,7 @@ class Home extends Component<PropsType, StateType> {
               onRequestClose={() => setCurrentModal(null, null)}
               onRequestClose={() => setCurrentModal(null, null)}
               width="600px"
               width="600px"
               height="220px"
               height="220px"
+              title="Add Namespace"
             >
             >
               <NamespaceModal />
               <NamespaceModal />
             </Modal>
             </Modal>
@@ -554,6 +557,7 @@ class Home extends Component<PropsType, StateType> {
               onRequestClose={() => setCurrentModal(null, null)}
               onRequestClose={() => setCurrentModal(null, null)}
               width="700px"
               width="700px"
               height="280px"
               height="280px"
+              title="Delete Namespace"
             >
             >
               <DeleteNamespaceModal />
               <DeleteNamespaceModal />
             </Modal>
             </Modal>
@@ -573,6 +577,7 @@ class Home extends Component<PropsType, StateType> {
             onRequestClose={() => setCurrentModal(null, null)}
             onRequestClose={() => setCurrentModal(null, null)}
             width="760px"
             width="760px"
             height="440px"
             height="440px"
+            title="Account Settings"
           >
           >
             <AccountSettingsModal />
             <AccountSettingsModal />
           </Modal>
           </Modal>
@@ -582,7 +587,8 @@ class Home extends Component<PropsType, StateType> {
           <Modal
           <Modal
             onRequestClose={() => setCurrentModal(null, null)}
             onRequestClose={() => setCurrentModal(null, null)}
             width="760px"
             width="760px"
-            height="440px"
+            height="530px"
+            title="Usage Warning"
           >
           >
             <UsageWarningModal />
             <UsageWarningModal />
           </Modal>
           </Modal>

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

@@ -22,7 +22,7 @@ const WelcomeForm: React.FunctionComponent<Props> = ({}) => {
 
 
   const submitForm = () => {
   const submitForm = () => {
     api
     api
-      .getWelcome(
+      .postWelcome(
         "<token>",
         "<token>",
         {
         {
           email: context.user && context.user.email,
           email: context.user && context.user.email,

+ 19 - 14
dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedChart.tsx

@@ -69,8 +69,9 @@ const ExpandedChart: React.FC<Props> = (props) => {
   const [rightTabOptions, setRightTabOptions] = useState<any[]>([]);
   const [rightTabOptions, setRightTabOptions] = useState<any[]>([]);
   const [leftTabOptions, setLeftTabOptions] = useState<any[]>([]);
   const [leftTabOptions, setLeftTabOptions] = useState<any[]>([]);
   const [saveValuesStatus, setSaveValueStatus] = useState<string>(null);
   const [saveValuesStatus, setSaveValueStatus] = useState<string>(null);
-  const [forceRefreshRevisions, setForceRefreshRevisions] =
-    useState<boolean>(false);
+  const [forceRefreshRevisions, setForceRefreshRevisions] = useState<boolean>(
+    false
+  );
   const [controllers, setControllers] = useState<
   const [controllers, setControllers] = useState<
     Record<string, Record<string, any>>
     Record<string, Record<string, any>>
   >({});
   >({});
@@ -82,11 +83,19 @@ const ExpandedChart: React.FC<Props> = (props) => {
   const [showRepoTooltip, setShowRepoTooltip] = useState(false);
   const [showRepoTooltip, setShowRepoTooltip] = useState(false);
   const [isAuthorized] = useAuth();
   const [isAuthorized] = useAuth();
 
 
-  const { newWebsocket, openWebsocket, closeAllWebsockets, closeWebsocket } =
-    useWebsockets();
+  const {
+    newWebsocket,
+    openWebsocket,
+    closeAllWebsockets,
+    closeWebsocket,
+  } = useWebsockets();
 
 
-  const { currentCluster, currentProject, setCurrentError, setCurrentOverlay } =
-    useContext(Context);
+  const {
+    currentCluster,
+    currentProject,
+    setCurrentError,
+    setCurrentOverlay,
+  } = useContext(Context);
 
 
   // Retrieve full chart data (includes form and values)
   // Retrieve full chart data (includes form and values)
   const getChartData = async (chart: ChartType) => {
   const getChartData = async (chart: ChartType) => {
@@ -196,8 +205,7 @@ const ExpandedChart: React.FC<Props> = (props) => {
     try {
     try {
       const res = await api.getChartComponents(
       const res = await api.getChartComponents(
         "<token>",
         "<token>",
-        {
-        },
+        {},
         {
         {
           id: currentProject.id,
           id: currentProject.id,
           name: currentChart.name,
           name: currentChart.name,
@@ -262,8 +270,7 @@ const ExpandedChart: React.FC<Props> = (props) => {
         values: valuesYaml,
         values: valuesYaml,
       });
       });
     } catch (err) {
     } catch (err) {
-      const parsedErr =
-        err?.response?.data?.error;
+      const parsedErr = err?.response?.data?.error;
 
 
       if (parsedErr) {
       if (parsedErr) {
         err = parsedErr;
         err = parsedErr;
@@ -317,8 +324,7 @@ const ExpandedChart: React.FC<Props> = (props) => {
 
 
         cb && cb();
         cb && cb();
       } catch (err) {
       } catch (err) {
-        let parsedErr =
-          err?.response?.data?.error;
+        let parsedErr = err?.response?.data?.error;
 
 
         if (parsedErr) {
         if (parsedErr) {
           err = parsedErr;
           err = parsedErr;
@@ -626,8 +632,7 @@ const ExpandedChart: React.FC<Props> = (props) => {
     api
     api
       .getIngress(
       .getIngress(
         "<token>",
         "<token>",
-        {
-        },
+        {},
         {
         {
           id: currentProject.id,
           id: currentProject.id,
           name: ingressName,
           name: ingressName,

+ 4 - 8
dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedJobChart.tsx

@@ -78,8 +78,7 @@ class ExpandedJobChart extends Component<PropsType, StateType> {
     api
     api
       .getChart(
       .getChart(
         "<token>",
         "<token>",
-        {
-        },
+        {},
         {
         {
           name: chart.name,
           name: chart.name,
           revision: revision,
           revision: revision,
@@ -380,8 +379,7 @@ class ExpandedJobChart extends Component<PropsType, StateType> {
         this.refreshChart(0);
         this.refreshChart(0);
       })
       })
       .catch((err) => {
       .catch((err) => {
-        let parsedErr =
-          err?.response?.data?.error;
+        let parsedErr = err?.response?.data?.error;
 
 
         if (parsedErr) {
         if (parsedErr) {
           err = parsedErr;
           err = parsedErr;
@@ -407,8 +405,7 @@ class ExpandedJobChart extends Component<PropsType, StateType> {
     api
     api
       .getJobs(
       .getJobs(
         "<token>",
         "<token>",
-        {
-        },
+        {},
         {
         {
           id: currentProject.id,
           id: currentProject.id,
           cluster_id: currentCluster.id,
           cluster_id: currentCluster.id,
@@ -631,8 +628,7 @@ class ExpandedJobChart extends Component<PropsType, StateType> {
 
 
       cb && cb();
       cb && cb();
     } catch (err) {
     } catch (err) {
-      let parsedErr =
-        err?.response?.data?.error;
+      let parsedErr = err?.response?.data?.error;
 
 
       if (parsedErr) {
       if (parsedErr) {
         err = parsedErr;
         err = parsedErr;

+ 5 - 4
dashboard/src/main/home/cluster-dashboard/expanded-chart/NotificationSettingsSection.tsx

@@ -39,8 +39,7 @@ const NotificationSettingsSection: React.FC<Props> = (props) => {
     api
     api
       .getNotificationConfig(
       .getNotificationConfig(
         "<token>",
         "<token>",
-        {
-        },
+        {},
         {
         {
           project_id: currentProject.id,
           project_id: currentProject.id,
           namespace: props.currentChart.namespace,
           namespace: props.currentChart.namespace,
@@ -103,7 +102,7 @@ const NotificationSettingsSection: React.FC<Props> = (props) => {
         setSaveLoading(false);
         setSaveLoading(false);
       });
       });
   };
   };
-  
+
   return (
   return (
     <>
     <>
       <Heading>Notification Settings</Heading>
       <Heading>Notification Settings</Heading>
@@ -120,7 +119,9 @@ const NotificationSettingsSection: React.FC<Props> = (props) => {
           {hasNotifications != null && !hasNotifications ? (
           {hasNotifications != null && !hasNotifications ? (
             <Banner type="warning">
             <Banner type="warning">
               No integration has been set up for notifications.{" "}
               No integration has been set up for notifications.{" "}
-              <A href={`${window.location.protocol}//${window.location.host}/integrations/slack`} >
+              <A
+                href={`${window.location.protocol}//${window.location.host}/integrations/slack`}
+              >
                 Connect to Slack
                 Connect to Slack
               </A>
               </A>
             </Banner>
             </Banner>

+ 7 - 3
dashboard/src/main/home/cluster-dashboard/expanded-chart/RevisionSection.tsx

@@ -47,13 +47,17 @@ class RevisionSection extends Component<PropsType, StateType> {
   refreshHistory = () => {
   refreshHistory = () => {
     let { chart } = this.props;
     let { chart } = this.props;
     let { currentCluster, currentProject } = this.context;
     let { currentCluster, currentProject } = this.context;
-    
+
     return api
     return api
       .getRevisions(
       .getRevisions(
         "<token>",
         "<token>",
+        {},
         {
         {
-        },
-        { id: currentProject.id, namespace: chart.namespace, cluster_id: currentCluster.id, name: chart.name }
+          id: currentProject.id,
+          namespace: chart.namespace,
+          cluster_id: currentCluster.id,
+          name: chart.name,
+        }
       )
       )
       .then((res) => {
       .then((res) => {
         res.data.sort((a: ChartType, b: ChartType) => {
         res.data.sort((a: ChartType, b: ChartType) => {

+ 11 - 12
dashboard/src/main/home/cluster-dashboard/expanded-chart/SettingsSection.tsx

@@ -65,10 +65,9 @@ const SettingsSection: React.FC<PropsType> = ({
     api
     api
       .getReleaseToken(
       .getReleaseToken(
         "<token>",
         "<token>",
+        {},
         {
         {
-        },
-        { 
-          id: currentProject.id, 
+          id: currentProject.id,
           name: currentChart?.name,
           name: currentChart?.name,
           namespace: currentChart?.namespace,
           namespace: currentChart?.namespace,
           cluster_id: currentCluster.id,
           cluster_id: currentCluster.id,
@@ -129,8 +128,7 @@ const SettingsSection: React.FC<PropsType> = ({
       setSaveValuesStatus("successful");
       setSaveValuesStatus("successful");
       refreshChart();
       refreshChart();
     } catch (err) {
     } catch (err) {
-      let parsedErr =
-        err?.response?.data?.error;
+      let parsedErr = err?.response?.data?.error;
 
 
       if (parsedErr) {
       if (parsedErr) {
         err = parsedErr;
         err = parsedErr;
@@ -163,8 +161,7 @@ const SettingsSection: React.FC<PropsType> = ({
         setWebhookToken(res.data.webhook_token);
         setWebhookToken(res.data.webhook_token);
       }, 500);
       }, 500);
     } catch (err) {
     } catch (err) {
-      let parsedErr =
-        err?.response?.data?.error;
+      let parsedErr = err?.response?.data?.error;
 
 
       if (parsedErr) {
       if (parsedErr) {
         err = parsedErr;
         err = parsedErr;
@@ -288,17 +285,19 @@ const SettingsSection: React.FC<PropsType> = ({
   };
   };
 
 
   const canBeCloned = () => {
   const canBeCloned = () => {
-    if(chartWasDeployedWithGithub()) {
+    if (chartWasDeployedWithGithub()) {
       return false;
       return false;
     }
     }
 
 
     // If its not web worker or job it means is an addon, and for now it's not supported
     // If its not web worker or job it means is an addon, and for now it's not supported
-    if (!["web", "worker", "job"].includes(currentChart?.chart?.metadata?.name)) {
-      return false
+    if (
+      !["web", "worker", "job"].includes(currentChart?.chart?.metadata?.name)
+    ) {
+      return false;
     }
     }
 
 
-    return true
-  }
+    return true;
+  };
 
 
   return (
   return (
     <Wrapper>
     <Wrapper>

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

@@ -77,8 +77,7 @@ export default class ValuesYaml extends Component<PropsType, StateType> {
         this.props.refreshChart();
         this.props.refreshChart();
       })
       })
       .catch((err) => {
       .catch((err) => {
-        let parsedErr =
-          err?.response?.data?.error;
+        let parsedErr = err?.response?.data?.error;
 
 
         if (parsedErr) {
         if (parsedErr) {
           err = parsedErr;
           err = parsedErr;

+ 21 - 26
dashboard/src/main/home/cluster-dashboard/expanded-chart/events/EventsTab.tsx

@@ -88,27 +88,26 @@ const EventsTab: React.FunctionComponent<Props> = (props) => {
       if (!shouldRequest) return;
       if (!shouldRequest) return;
       setShouldRequest(false);
       setShouldRequest(false);
       api
       api
-          .getReleaseSteps(
-              "<token>",
-              {
-              },
-              {
-                cluster_id: currentCluster.id,
-                namespace: props.currentChart.namespace,
-                id: currentProject.id,
-                name: props.currentChart.name,
-              }
-          )
-          .then((data) => {
-            setIsLoading(false);
-            filterData(data.data);
-          })
-          .catch((err) => {
-            setIsError(true);
-          })
-          .finally(() => {
-            setShouldRequest(true);
-          });
+        .getReleaseSteps(
+          "<token>",
+          {},
+          {
+            cluster_id: currentCluster.id,
+            namespace: props.currentChart.namespace,
+            id: currentProject.id,
+            name: props.currentChart.name,
+          }
+        )
+        .then((data) => {
+          setIsLoading(false);
+          filterData(data.data);
+        })
+        .catch((err) => {
+          setIsError(true);
+        })
+        .finally(() => {
+          setShouldRequest(true);
+        });
     };
     };
 
 
     getData();
     getData();
@@ -121,11 +120,7 @@ const EventsTab: React.FunctionComponent<Props> = (props) => {
   }, [currentProject, currentCluster, props.currentChart]);
   }, [currentProject, currentCluster, props.currentChart]);
 
 
   if (isError) {
   if (isError) {
-    return (
-        <Placeholder>
-          Error loading events.
-        </Placeholder>
-    )
+    return <Placeholder>Error loading events.</Placeholder>;
   }
   }
 
 
   if (isLoading) {
   if (isLoading) {

+ 2 - 4
dashboard/src/main/home/cluster-dashboard/expanded-chart/jobs/JobList.tsx

@@ -76,8 +76,7 @@ class JobList extends Component<PropsType, StateType> {
     api
     api
       .deleteJob(
       .deleteJob(
         "<token>",
         "<token>",
-        {
-        },
+        {},
         {
         {
           id: currentProject.id,
           id: currentProject.id,
           name: job.metadata?.name,
           name: job.metadata?.name,
@@ -92,8 +91,7 @@ class JobList extends Component<PropsType, StateType> {
         });
         });
       })
       })
       .catch((err) => {
       .catch((err) => {
-        let parsedErr =
-          err?.response?.data?.error;
+        let parsedErr = err?.response?.data?.error;
         if (parsedErr) {
         if (parsedErr) {
           err = parsedErr;
           err = parsedErr;
         }
         }

+ 2 - 4
dashboard/src/main/home/cluster-dashboard/expanded-chart/jobs/JobResource.tsx

@@ -59,8 +59,7 @@ export default class JobResource extends Component<PropsType, StateType> {
       )
       )
       .then((res) => {})
       .then((res) => {})
       .catch((err) => {
       .catch((err) => {
-        let parsedErr =
-          err?.response?.data?.error;
+        let parsedErr = err?.response?.data?.error;
         if (parsedErr) {
         if (parsedErr) {
           err = parsedErr;
           err = parsedErr;
         }
         }
@@ -74,8 +73,7 @@ export default class JobResource extends Component<PropsType, StateType> {
     api
     api
       .getJobPods(
       .getJobPods(
         "<token>",
         "<token>",
-        {
-        },
+        {},
         {
         {
           id: currentProject.id,
           id: currentProject.id,
           name: this.props.job.metadata?.name,
           name: this.props.job.metadata?.name,

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

@@ -136,8 +136,7 @@ const MetricsSection: React.FunctionComponent<PropsType> = ({
     api
     api
       .getChartControllers(
       .getChartControllers(
         "<token>",
         "<token>",
-        {
-        },
+        {},
         {
         {
           id: currentProject.id,
           id: currentProject.id,
           name: currentChart.name,
           name: currentChart.name,

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

@@ -230,8 +230,7 @@ const ControllerTabFC: React.FunctionComponent<Props> = ({
     api
     api
       .deletePod(
       .deletePod(
         "<token>",
         "<token>",
-        {
-        },
+        {},
         {
         {
           cluster_id: currentCluster.id,
           cluster_id: currentCluster.id,
           name: pod.metadata?.name,
           name: pod.metadata?.name,

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

@@ -180,8 +180,7 @@ export default class Logs extends Component<PropsType, StateType> {
     api
     api
       .getPodEvents(
       .getPodEvents(
         "<token>",
         "<token>",
-        {
-        },
+        {},
         {
         {
           name: selectedPod?.metadata?.name,
           name: selectedPod?.metadata?.name,
           namespace: selectedPod?.metadata?.namespace,
           namespace: selectedPod?.metadata?.namespace,

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

@@ -32,8 +32,7 @@ const StatusSectionFC: React.FunctionComponent<Props> = ({
     api
     api
       .getChartControllers(
       .getChartControllers(
         "<token>",
         "<token>",
-        {
-        },
+        {},
         {
         {
           namespace: currentChart.namespace,
           namespace: currentChart.namespace,
           cluster_id: currentCluster.id,
           cluster_id: currentCluster.id,

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

@@ -168,7 +168,7 @@ class Templates extends Component<PropsType, StateType> {
 
 
   areCloneQueryParamsValid = () => {
   areCloneQueryParamsValid = () => {
     const qp = getQueryParams(this.props);
     const qp = getQueryParams(this.props);
-    
+
     const requiredParams = [
     const requiredParams = [
       "release_namespace",
       "release_namespace",
       "release_template_version",
       "release_template_version",
@@ -325,7 +325,7 @@ class Templates extends Component<PropsType, StateType> {
         <TemplatesWrapper>
         <TemplatesWrapper>
           <TitleSection>
           <TitleSection>
             Launch
             Launch
-            <a href="https://docs.getporter.dev/docs/add-ons" target="_blank">
+            <a href="https://docs.porter.run/docs/addons" target="_blank">
               <i className="material-icons">help_outline</i>
               <i className="material-icons">help_outline</i>
             </a>
             </a>
           </TitleSection>
           </TitleSection>

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

@@ -275,7 +275,7 @@ class SourcePage extends Component<PropsType, StateType> {
         <Helper>
         <Helper>
           Learn more about
           Learn more about
           <Highlight
           <Highlight
-            href="https://docs.getporter.dev/docs/add-ons"
+            href="https://docs.porter.run/docs/addons"
             target="_blank"
             target="_blank"
           >
           >
             deploying services to Porter
             deploying services to Porter

+ 0 - 72
dashboard/src/main/home/modals/AccountSettingsModal.tsx

@@ -1,7 +1,6 @@
 import React, { useContext, useEffect, useState } from "react";
 import React, { useContext, useEffect, useState } from "react";
 import styled from "styled-components";
 import styled from "styled-components";
 
 
-import close from "assets/close.png";
 import github from "assets/github.png";
 import github from "assets/github.png";
 
 
 import { Context } from "../../../shared/Context";
 import { Context } from "../../../shared/Context";
@@ -42,15 +41,6 @@ const AccountSettingsModal = () => {
 
 
   return (
   return (
     <>
     <>
-      <CloseButton
-        onClick={() => {
-          setCurrentModal(null, null);
-        }}
-      >
-        <CloseButtonImg src={close} />
-      </CloseButton>
-      <ModalTitle>Account Settings</ModalTitle>
-
       <TabSelector
       <TabSelector
         options={tabOptions}
         options={tabOptions}
         currentTab={currentTab}
         currentTab={currentTab}
@@ -181,68 +171,6 @@ const GitIcon = styled.img`
   margin-left: 1px;
   margin-left: 1px;
 `;
 `;
 
 
-const ModalTitle = styled.div`
-  margin: 0px 0px 13px;
-  display: flex;
-  flex: 1;
-  font-family: Work Sans, sans-serif;
-  font-size: 18px;
-  color: #ffffff;
-  user-select: none;
-  font-weight: 700;
-  align-items: center;
-  position: relative;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-
-  > i {
-    background: none;
-    border-radius: 3px;
-    display: flex;
-    font-size: 18px;
-    margin-top: 1px;
-    margin-right: 10px;
-    padding: 1px;
-    align-items: center;
-    justify-content: center;
-    color: #ffffffaa;
-    border: 0;
-  }
-`;
-
-const Subtitle = styled.div`
-  margin-top: 23px;
-  font-family: "Work Sans", sans-serif;
-  font-size: 13px;
-  color: #aaaabb;
-  overflow: hidden;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-  margin-bottom: -10px;
-`;
-
-const CloseButton = styled.div`
-  position: absolute;
-  display: block;
-  width: 40px;
-  height: 40px;
-  padding: 13px 0 12px 0;
-  z-index: 1;
-  text-align: center;
-  border-radius: 50%;
-  right: 15px;
-  top: 12px;
-  cursor: pointer;
-  :hover {
-    background-color: #ffffff11;
-  }
-`;
-
-const CloseButtonImg = styled.img`
-  width: 14px;
-  margin: 0 auto;
-`;
-
 const A = styled.a`
 const A = styled.a`
   color: #8590ff;
   color: #8590ff;
   text-decoration: underline;
   text-decoration: underline;

+ 5 - 75
dashboard/src/main/home/modals/ClusterInstructionsModal.tsx

@@ -34,8 +34,8 @@ export default class ClusterInstructionsModal extends Component<
               <br />
               <br />
               name=$(curl -s
               name=$(curl -s
               https://api.github.com/repos/porter-dev/porter/releases/latest |
               https://api.github.com/repos/porter-dev/porter/releases/latest |
-              grep "browser_download_url.*/porter_.*_Darwin_x86_64\.zip" | cut -d
-              ":" -f 2,3 | tr -d \")
+              grep "browser_download_url.*/porter_.*_Darwin_x86_64\.zip" | cut
+              -d ":" -f 2,3 | tr -d \")
               <br />
               <br />
               name=$(basename $name)
               name=$(basename $name)
               <br />
               <br />
@@ -96,17 +96,7 @@ export default class ClusterInstructionsModal extends Component<
   render() {
   render() {
     let { currentPage, currentTab } = this.state;
     let { currentPage, currentTab } = this.state;
     return (
     return (
-      <StyledClusterInstructionsModal>
-        <CloseButton
-          onClick={() => {
-            this.context.setCurrentModal(null, null);
-          }}
-        >
-          <CloseButtonImg src={close} />
-        </CloseButton>
-
-        <ModalTitle>Connecting to an Existing Cluster</ModalTitle>
-
+      <>
         <TabSelector
         <TabSelector
           options={tabOptions}
           options={tabOptions}
           currentTab={currentTab}
           currentTab={currentTab}
@@ -139,7 +129,7 @@ export default class ClusterInstructionsModal extends Component<
             arrow_forward
             arrow_forward
           </i>
           </i>
         </PageSection>
         </PageSection>
-      </StyledClusterInstructionsModal>
+      </>
     );
     );
   }
   }
 }
 }
@@ -207,64 +197,4 @@ const Placeholder = styled.div`
 const Bold = styled.div`
 const Bold = styled.div`
   font-weight: 600;
   font-weight: 600;
   margin-bottom: 7px;
   margin-bottom: 7px;
-`;
-
-const Subtitle = styled.div`
-  padding: 17px 0px 25px;
-  font-family: "Work Sans", sans-serif;
-  font-size: 13px;
-  color: #aaaabb;
-  margin-top: 3px;
-  overflow: hidden;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-`;
-
-const ModalTitle = styled.div`
-  margin: 0px 0px 13px;
-  display: flex;
-  flex: 1;
-  font-family: Work Sans, sans-serif;
-  font-size: 18px;
-  color: #ffffff;
-  user-select: none;
-  font-weight: 700;
-  align-items: center;
-  position: relative;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-`;
-
-const CloseButton = styled.div`
-  position: absolute;
-  display: block;
-  width: 40px;
-  height: 40px;
-  padding: 13px 0 12px 0;
-  z-index: 1;
-  text-align: center;
-  border-radius: 50%;
-  right: 15px;
-  top: 12px;
-  cursor: pointer;
-  :hover {
-    background-color: #ffffff11;
-  }
-`;
-
-const CloseButtonImg = styled.img`
-  width: 14px;
-  margin: 0 auto;
-`;
-
-const StyledClusterInstructionsModal = styled.div`
-  width: 100%;
-  position: absolute;
-  left: 0;
-  top: 0;
-  height: 100%;
-  padding: 25px 32px;
-  overflow: hidden;
-  border-radius: 6px;
-  background: #202227;
-`;
+`;

+ 3 - 61
dashboard/src/main/home/modals/DeleteNamespaceModal.tsx

@@ -20,7 +20,7 @@ const DeleteNamespaceModal = () => {
   const [status, setStatus] = useState<string>(null as string);
   const [status, setStatus] = useState<string>(null as string);
   const deleteNamespace = () => {
   const deleteNamespace = () => {
     if (namespaceNameForDelition !== currentModalData.metadata.name) {
     if (namespaceNameForDelition !== currentModalData.metadata.name) {
-      setStatus("Please insert the name of the namespace to confirm deletion");
+      setStatus("Please enter the name of this namespace to confirm deletion");
       return;
       return;
     }
     }
 
 
@@ -47,16 +47,7 @@ const DeleteNamespaceModal = () => {
   };
   };
 
 
   return (
   return (
-    <StyledUpdateProjectModal>
-      <CloseButton
-        onClick={() => {
-          setCurrentModal(null, null);
-        }}
-      >
-        <CloseButtonImg src={close} />
-      </CloseButton>
-
-      <ModalTitle>Delete Namespace</ModalTitle>
+    <>
       <Subtitle>
       <Subtitle>
         Please insert the name of the namespace to delete it:
         Please insert the name of the namespace to delete it:
         <DangerText>{" " + currentModalData.metadata.name}</DangerText>
         <DangerText>{" " + currentModalData.metadata.name}</DangerText>
@@ -84,7 +75,7 @@ const DeleteNamespaceModal = () => {
         onClick={() => deleteNamespace()}
         onClick={() => deleteNamespace()}
         status={status}
         status={status}
       />
       />
-    </StyledUpdateProjectModal>
+    </>
   );
   );
 };
 };
 
 
@@ -133,55 +124,6 @@ const Subtitle = styled.div`
   margin-bottom: -10px;
   margin-bottom: -10px;
 `;
 `;
 
 
-const ModalTitle = styled.div`
-  margin: 0px 0px 13px;
-  display: flex;
-  flex: 1;
-  font-family: Work Sans, sans-serif;
-  font-size: 18px;
-  color: #ffffff;
-  user-select: none;
-  font-weight: 700;
-  align-items: center;
-  position: relative;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-`;
-
-const CloseButton = styled.div`
-  position: absolute;
-  display: block;
-  width: 40px;
-  height: 40px;
-  padding: 13px 0 12px 0;
-  z-index: 1;
-  text-align: center;
-  border-radius: 50%;
-  right: 15px;
-  top: 12px;
-  cursor: pointer;
-  :hover {
-    background-color: #ffffff11;
-  }
-`;
-
-const CloseButtonImg = styled.img`
-  width: 14px;
-  margin: 0 auto;
-`;
-
-const StyledUpdateProjectModal = styled.div`
-  width: 100%;
-  position: absolute;
-  left: 0;
-  top: 0;
-  height: 100%;
-  padding: 25px 30px;
-  overflow: hidden;
-  border-radius: 6px;
-  background: #202227;
-`;
-
 const Warning = styled.div`
 const Warning = styled.div`
   font-size: 13px;
   font-size: 13px;
   display: flex;
   display: flex;

+ 5 - 52
dashboard/src/main/home/modals/EditInviteOrCollaboratorModal.tsx

@@ -80,15 +80,7 @@ const EditCollaboratorModal = () => {
   };
   };
 
 
   return (
   return (
-    <StyledUpdateProjectModal>
-      <CloseButton
-        onClick={() => {
-          setCurrentModal(null, null);
-        }}
-      >
-        <CloseButtonImg src={close} />
-      </CloseButton>
-
+    <>
       <ModalTitle>
       <ModalTitle>
         Update {isInvite ? "Invite for" : "Collaborator"} {user?.email}
         Update {isInvite ? "Invite for" : "Collaborator"} {user?.email}
       </ModalTitle>
       </ModalTitle>
@@ -107,7 +99,7 @@ const EditCollaboratorModal = () => {
         onClick={() => handleUpdate()}
         onClick={() => handleUpdate()}
         status={status}
         status={status}
       />
       />
-    </StyledUpdateProjectModal>
+    </>
   );
   );
 };
 };
 
 
@@ -130,50 +122,11 @@ const Subtitle = styled.div`
 `;
 `;
 
 
 const ModalTitle = styled.div`
 const ModalTitle = styled.div`
-  margin: 0px 0px 13px;
-  display: flex;
-  flex: 1;
-  font-family: "Work Sans", sans-serif;
   font-size: 18px;
   font-size: 18px;
-  color: #ffffff;
+  font-weight: 500;
+  margin-bottom: 10px;
   user-select: none;
   user-select: none;
-  font-weight: 700;
-  align-items: center;
   position: relative;
   position: relative;
   white-space: nowrap;
   white-space: nowrap;
   text-overflow: ellipsis;
   text-overflow: ellipsis;
-`;
-
-const CloseButton = styled.div`
-  position: absolute;
-  display: block;
-  width: 40px;
-  height: 40px;
-  padding: 13px 0 12px 0;
-  z-index: 1;
-  text-align: center;
-  border-radius: 50%;
-  right: 15px;
-  top: 12px;
-  cursor: pointer;
-  :hover {
-    background-color: #ffffff11;
-  }
-`;
-
-const CloseButtonImg = styled.img`
-  width: 14px;
-  margin: 0 auto;
-`;
-
-const StyledUpdateProjectModal = styled.div`
-  width: 100%;
-  position: absolute;
-  left: 0;
-  top: 0;
-  height: 100%;
-  padding: 25px 30px;
-  overflow: hidden;
-  border-radius: 6px;
-  background: #202227;
-`;
+`;

+ 3 - 109
dashboard/src/main/home/modals/IntegrationsInstructionsModal.tsx

@@ -47,17 +47,7 @@ export default class ClusterInstructionsModal extends Component<
   render() {
   render() {
     let { currentPage, currentTab } = this.state;
     let { currentPage, currentTab } = this.state;
     return (
     return (
-      <StyledClusterInstructionsModal>
-        <CloseButton
-          onClick={() => {
-            this.context.setCurrentModal(null, null);
-          }}
-        >
-          <CloseButtonImg src={close} />
-        </CloseButton>
-
-        <ModalTitle>Connecting to an Image Registry</ModalTitle>
-
+      <>
         <TabSelector
         <TabSelector
           options={tabOptions}
           options={tabOptions}
           currentTab={currentTab}
           currentTab={currentTab}
@@ -67,42 +57,13 @@ export default class ClusterInstructionsModal extends Component<
         />
         />
 
 
         {this.renderPage()}
         {this.renderPage()}
-      </StyledClusterInstructionsModal>
+      </>
     );
     );
   }
   }
 }
 }
 
 
 ClusterInstructionsModal.contextType = Context;
 ClusterInstructionsModal.contextType = Context;
 
 
-const PageCount = styled.div`
-  margin-right: 9px;
-  user-select: none;
-  letter-spacing: 2px;
-`;
-
-const PageSection = styled.div`
-  position: absolute;
-  bottom: 22px;
-  right: 20px;
-  display: flex;
-  align-items: center;
-  font-size: 13px;
-  color: #ffffff;
-  justify-content: flex-end;
-  user-select: none;
-
-  > i {
-    font-size: 18px;
-    margin-left: 2px;
-    cursor: pointer;
-    border-radius: 20px;
-    padding: 5px;
-    :hover {
-      background: #ffffff11;
-    }
-  }
-`;
-
 const Code = styled.div`
 const Code = styled.div`
   background: #181b21;
   background: #181b21;
   padding: 10px 15px;
   padding: 10px 15px;
@@ -116,13 +77,6 @@ const Code = styled.div`
   font-family: monospace;
   font-family: monospace;
 `;
 `;
 
 
-const A = styled.a`
-  color: #ffffff;
-  text-decoration: underline;
-  cursor: ${(props: { disabled?: boolean }) =>
-    props.disabled ? "not-allowed" : "pointer"};
-`;
-
 const Placeholder = styled.div`
 const Placeholder = styled.div`
   color: #aaaabb;
   color: #aaaabb;
   font-size: 13px;
   font-size: 13px;
@@ -135,64 +89,4 @@ const Placeholder = styled.div`
 const Bold = styled.div`
 const Bold = styled.div`
   font-weight: 600;
   font-weight: 600;
   margin-bottom: 7px;
   margin-bottom: 7px;
-`;
-
-const Subtitle = styled.div`
-  padding: 10px 0px 20px;
-  font-family: "Work Sans", sans-serif;
-  font-size: 13px;
-  color: #aaaabb;
-  margin-top: 3px;
-  overflow: hidden;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-`;
-
-const ModalTitle = styled.div`
-  margin: 0px 0px 13px;
-  display: flex;
-  flex: 1;
-  font-family: Work Sans, sans-serif;
-  font-size: 18px;
-  color: #ffffff;
-  user-select: none;
-  font-weight: 700;
-  align-items: center;
-  position: relative;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-`;
-
-const CloseButton = styled.div`
-  position: absolute;
-  display: block;
-  width: 40px;
-  height: 40px;
-  padding: 13px 0 12px 0;
-  z-index: 1;
-  text-align: center;
-  border-radius: 50%;
-  right: 15px;
-  top: 12px;
-  cursor: pointer;
-  :hover {
-    background-color: #ffffff11;
-  }
-`;
-
-const CloseButtonImg = styled.img`
-  width: 14px;
-  margin: 0 auto;
-`;
-
-const StyledClusterInstructionsModal = styled.div`
-  width: 100%;
-  position: absolute;
-  left: 0;
-  top: 0;
-  height: 100%;
-  padding: 25px 32px;
-  overflow: hidden;
-  border-radius: 6px;
-  background: #202227;
-`;
+`;

+ 7 - 63
dashboard/src/main/home/modals/IntegrationsModal.tsx

@@ -46,7 +46,9 @@ export default class IntegrationsModal extends Component<PropsType, StateType> {
           integrationList[integration.service].icon;
           integrationList[integration.service].icon;
         let disabled =
         let disabled =
           integration.service === "kube" || integration.service === "dockerhub";
           integration.service === "kube" || integration.service === "dockerhub";
-        return (
+
+        if (!disabled) {
+          return (
           <IntegrationOption
           <IntegrationOption
             key={i}
             key={i}
             disabled={disabled}
             disabled={disabled}
@@ -61,28 +63,19 @@ export default class IntegrationsModal extends Component<PropsType, StateType> {
             <Label>{integrationList[integration.service].label}</Label>
             <Label>{integrationList[integration.service].label}</Label>
           </IntegrationOption>
           </IntegrationOption>
         );
         );
+        }
       });
       });
     }
     }
   };
   };
 
 
   render() {
   render() {
     return (
     return (
-      <StyledIntegrationsModal>
-        <CloseButton
-          onClick={() => {
-            this.context.setCurrentModal(null, null);
-          }}
-        >
-          <CloseButtonImg src={close} />
-        </CloseButton>
-
-        <ModalTitle>Add a New Integration</ModalTitle>
+      <>
         <Subtitle>Select the service you would like to connect to.</Subtitle>
         <Subtitle>Select the service you would like to connect to.</Subtitle>
-
         <IntegrationsCatalog>
         <IntegrationsCatalog>
           {this.renderIntegrationsCatalog()}
           {this.renderIntegrationsCatalog()}
         </IntegrationsCatalog>
         </IntegrationsCatalog>
-      </StyledIntegrationsModal>
+      </>
     );
     );
   }
   }
 }
 }
@@ -134,53 +127,4 @@ const Subtitle = styled.div`
   overflow: hidden;
   overflow: hidden;
   white-space: nowrap;
   white-space: nowrap;
   text-overflow: ellipsis;
   text-overflow: ellipsis;
-`;
-
-const ModalTitle = styled.div`
-  margin: 0px 0px 13px;
-  display: flex;
-  flex: 1;
-  font-family: Work Sans, sans-serif;
-  font-size: 18px;
-  color: #ffffff;
-  user-select: none;
-  font-weight: 700;
-  align-items: center;
-  position: relative;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-`;
-
-const CloseButton = styled.div`
-  position: absolute;
-  display: block;
-  width: 40px;
-  height: 40px;
-  padding: 13px 0 12px 0;
-  z-index: 1;
-  text-align: center;
-  border-radius: 50%;
-  right: 15px;
-  top: 12px;
-  cursor: pointer;
-  :hover {
-    background-color: #ffffff11;
-  }
-`;
-
-const CloseButtonImg = styled.img`
-  width: 14px;
-  margin: 0 auto;
-`;
-
-const StyledIntegrationsModal = styled.div`
-  width: 100%;
-  position: absolute;
-  left: 0;
-  top: 0;
-  height: 100%;
-  padding: 25px 32px;
-  overflow: hidden;
-  border-radius: 6px;
-  background: #202227;
-`;
+`;

+ 44 - 3
dashboard/src/main/home/modals/Modal.tsx

@@ -5,6 +5,7 @@ type PropsType = {
   onRequestClose: () => void;
   onRequestClose: () => void;
   width?: string;
   width?: string;
   height?: string;
   height?: string;
+  title?: string;
 };
 };
 
 
 type StateType = {};
 type StateType = {};
@@ -38,6 +39,14 @@ export default class Modal extends Component<PropsType, StateType> {
     return (
     return (
       <Overlay>
       <Overlay>
         <StyledModal ref={this.wrapperRef} width={width} height={height}>
         <StyledModal ref={this.wrapperRef} width={width} height={height}>
+          <CloseButton onClick={this.props.onRequestClose}>
+            <i className="material-icons">close</i>
+          </CloseButton>
+          { 
+            this.props.title && (
+              <ModalTitle>{this.props.title}</ModalTitle>
+            )
+          }
           {this.props.children}
           {this.props.children}
         </StyledModal>
         </StyledModal>
       </Overlay>
       </Overlay>
@@ -45,6 +54,36 @@ export default class Modal extends Component<PropsType, StateType> {
   }
   }
 }
 }
 
 
+const ModalTitle = styled.div`
+  font-size: 18px;
+  font-weight: 500;
+  margin-bottom: 10px;
+  user-select: none;
+`;
+
+const CloseButton = styled.div`
+  position: absolute;
+  display: block;
+  width: 40px;
+  height: 40px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  z-index: 1;
+  border-radius: 50%;
+  right: 15px;
+  top: 12px;
+  cursor: pointer;
+  :hover {
+    background-color: #ffffff11;
+  }
+
+  > i {
+    font-size: 20px;
+    color: #aaaabb;
+  }
+`;
+
 const Overlay = styled.div`
 const Overlay = styled.div`
   position: fixed;
   position: fixed;
   margin: 0;
   margin: 0;
@@ -67,11 +106,13 @@ const StyledModal = styled.div`
   max-width: 80vw;
   max-width: 80vw;
   height: ${(props: { width?: string; height?: string }) =>
   height: ${(props: { width?: string; height?: string }) =>
     props.height ? props.height : "425px"};
     props.height ? props.height : "425px"};
-  border-radius: 7px;
-  border: 0;
-  background-color: #202227;
   overflow: visible;
   overflow: visible;
   padding: 25px 32px;
   padding: 25px 32px;
+  font-size: 13px;
+  border-radius: 10px;
+  background: #202227;
+  border: 1px solid #ffffff55;
+  color: #ffffff;
   animation: floatInModal 0.5s 0s;
   animation: floatInModal 0.5s 0s;
   @keyframes floatInModal {
   @keyframes floatInModal {
     from {
     from {

+ 3 - 61
dashboard/src/main/home/modals/NamespaceModal.tsx

@@ -80,16 +80,7 @@ export default class NamespaceModal extends Component<PropsType, StateType> {
 
 
   render() {
   render() {
     return (
     return (
-      <StyledUpdateProjectModal>
-        <CloseButton
-          onClick={() => {
-            this.context.setCurrentModal(null, null);
-          }}
-        >
-          <CloseButtonImg src={close} />
-        </CloseButton>
-
-        <ModalTitle>Add Namespace</ModalTitle>
+      <>
         <Subtitle>Name</Subtitle>
         <Subtitle>Name</Subtitle>
 
 
         <InputWrapper>
         <InputWrapper>
@@ -113,7 +104,7 @@ export default class NamespaceModal extends Component<PropsType, StateType> {
           onClick={() => this.createNamespace()}
           onClick={() => this.createNamespace()}
           status={this.state.status}
           status={this.state.status}
         />
         />
-      </StyledUpdateProjectModal>
+      </>
     );
     );
   }
   }
 }
 }
@@ -156,53 +147,4 @@ const Subtitle = styled.div`
   white-space: nowrap;
   white-space: nowrap;
   text-overflow: ellipsis;
   text-overflow: ellipsis;
   margin-bottom: -10px;
   margin-bottom: -10px;
-`;
-
-const ModalTitle = styled.div`
-  margin: 0px 0px 13px;
-  display: flex;
-  flex: 1;
-  font-family: Work Sans, sans-serif;
-  font-size: 18px;
-  color: #ffffff;
-  user-select: none;
-  font-weight: 700;
-  align-items: center;
-  position: relative;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-`;
-
-const CloseButton = styled.div`
-  position: absolute;
-  display: block;
-  width: 40px;
-  height: 40px;
-  padding: 13px 0 12px 0;
-  z-index: 1;
-  text-align: center;
-  border-radius: 50%;
-  right: 15px;
-  top: 12px;
-  cursor: pointer;
-  :hover {
-    background-color: #ffffff11;
-  }
-`;
-
-const CloseButtonImg = styled.img`
-  width: 14px;
-  margin: 0 auto;
-`;
-
-const StyledUpdateProjectModal = styled.div`
-  width: 100%;
-  position: absolute;
-  left: 0;
-  top: 0;
-  height: 100%;
-  padding: 25px 30px;
-  overflow: hidden;
-  border-radius: 6px;
-  background: #202227;
-`;
+`;

+ 16 - 71
dashboard/src/main/home/modals/UpdateClusterModal.tsx

@@ -59,16 +59,19 @@ class UpdateClusterModal extends Component<PropsType, StateType> {
         }
         }
 
 
         // Handle destroying infra we've provisioned
         // Handle destroying infra we've provisioned
-        api.destroyInfra(
-          "<token>",
-                { name: currentCluster.name },
-                {
-                  project_id: currentProject.id,
-                  infra_id: currentCluster.infra_id,
-                }
-        ).then(() =>
-          console.log("destroyed provisioned infra:", currentCluster.infra_id)
-        ).catch(console.log);
+        api
+          .destroyInfra(
+            "<token>",
+            { name: currentCluster.name },
+            {
+              project_id: currentProject.id,
+              infra_id: currentCluster.infra_id,
+            }
+          )
+          .then(() =>
+            console.log("destroyed provisioned infra:", currentCluster.infra_id)
+          )
+          .catch(console.log);
 
 
         this.props.setRefreshClusters(true);
         this.props.setRefreshClusters(true);
         this.setState({ status: "successful", showDeleteOverlay: false });
         this.setState({ status: "successful", showDeleteOverlay: false });
@@ -99,16 +102,7 @@ class UpdateClusterModal extends Component<PropsType, StateType> {
 
 
   render() {
   render() {
     return (
     return (
-      <StyledUpdateProjectModal>
-        <CloseButton
-          onClick={() => {
-            this.context.setCurrentModal(null, null);
-          }}
-        >
-          <CloseButtonImg src={close} />
-        </CloseButton>
-
-        <ModalTitle>Cluster Settings</ModalTitle>
+      <>
         <Subtitle>Cluster name</Subtitle>
         <Subtitle>Cluster name</Subtitle>
 
 
         <InputWrapper>
         <InputWrapper>
@@ -147,7 +141,7 @@ class UpdateClusterModal extends Component<PropsType, StateType> {
           onYes={this.handleDelete}
           onYes={this.handleDelete}
           onNo={() => this.setState({ showDeleteOverlay: false })}
           onNo={() => this.setState({ showDeleteOverlay: false })}
         />
         />
-      </StyledUpdateProjectModal>
+      </>
     );
     );
   }
   }
 }
 }
@@ -229,53 +223,4 @@ const Subtitle = styled.div`
   white-space: nowrap;
   white-space: nowrap;
   text-overflow: ellipsis;
   text-overflow: ellipsis;
   margin-bottom: -10px;
   margin-bottom: -10px;
-`;
-
-const ModalTitle = styled.div`
-  margin: 0px 0px 13px;
-  display: flex;
-  flex: 1;
-  font-family: Work Sans, sans-serif;
-  font-size: 18px;
-  color: #ffffff;
-  user-select: none;
-  font-weight: 700;
-  align-items: center;
-  position: relative;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-`;
-
-const CloseButton = styled.div`
-  position: absolute;
-  display: block;
-  width: 40px;
-  height: 40px;
-  padding: 13px 0 12px 0;
-  z-index: 1;
-  text-align: center;
-  border-radius: 50%;
-  right: 15px;
-  top: 12px;
-  cursor: pointer;
-  :hover {
-    background-color: #ffffff11;
-  }
-`;
-
-const CloseButtonImg = styled.img`
-  width: 14px;
-  margin: 0 auto;
-`;
-
-const StyledUpdateProjectModal = styled.div`
-  width: 100%;
-  position: absolute;
-  left: 0;
-  top: 0;
-  height: 100%;
-  padding: 25px 30px;
-  overflow: hidden;
-  border-radius: 6px;
-  background: #202227;
-`;
+`;

+ 89 - 89
dashboard/src/main/home/modals/UsageWarningModal.tsx

@@ -1,6 +1,7 @@
 import React, { useContext, useEffect, useState } from "react";
 import React, { useContext, useEffect, useState } from "react";
 import styled from "styled-components";
 import styled from "styled-components";
-import close from "assets/close.png";
+
+import Banner from "components/Banner";
 
 
 import { Context } from "shared/Context";
 import { Context } from "shared/Context";
 import { UsageData } from "shared/types";
 import { UsageData } from "shared/types";
@@ -16,7 +17,6 @@ const ReadableNameMap: {
 };
 };
 
 
 const filterExceeded = (usage: UsageData) => {
 const filterExceeded = (usage: UsageData) => {
-  console.log(usage);
   const current = usage.current;
   const current = usage.current;
   const limits = usage.limit;
   const limits = usage.limit;
   return Object.keys(usage.current).reduce((acc, key) => {
   return Object.keys(usage.current).reduce((acc, key) => {
@@ -48,7 +48,7 @@ const UpgradeChartModal: React.FC<{}> = () => {
 
 
   useEffect(() => {
   useEffect(() => {
     if (usage) {
     if (usage) {
-      setFilteredUsage(filterExceeded(usage));
+      setFilteredUsage(usage);
     }
     }
   }, [usage]);
   }, [usage]);
 
 
@@ -57,36 +57,56 @@ const UpgradeChartModal: React.FC<{}> = () => {
   }
   }
   console.log({ usage, filteredUsage });
   console.log({ usage, filteredUsage });
   return (
   return (
-    <StyledUpgradeChartModal>
-      <CloseButton onClick={() => setCurrentModal(null, null)}>
-        <CloseButtonImg src={close} />
-      </CloseButton>
-      <ModalTitle>Usage warning</ModalTitle>
-      You're current project is currently exceeding its usage limits. Your usage
-      limits are:
-      <DescriptionSection>
-        {filteredUsage !== null &&
-          Object.entries(filteredUsage.limit).map(([key, value]) => {
-            return (
-              <div key={key}>
-                <b>{ReadableNameMap[key]}:</b> {value}
-              </div>
-            );
-          })}
-      </DescriptionSection>
-      Your project is currently using:
-      <DescriptionSection>
-        {filteredUsage !== null &&
-          Object.entries(filteredUsage.current).map(([key, value]) => {
-            return (
-              <div key={key}>
-                <b>{ReadableNameMap[key]}:</b> {value}
-              </div>
-            );
-          })}
-      </DescriptionSection>
-      You have currently <b>7 days</b> to resolve this issue before you loose
-      access to the Porter dashboard.
+    <>
+      <Br />
+        <Banner type="warning">
+          Your project is currently exceeding its resource usage limit.
+        </Banner>
+      <Br />
+      {
+        filteredUsage !== null && (
+          <UsageSection>
+            <UsageBlock isRed={filteredUsage.current["resource_cpu"] > filteredUsage.limit["resource_cpu"]}>
+              <Label isRed={filteredUsage.current["resource_cpu"] > filteredUsage.limit["resource_cpu"]}>
+                CPU Usage
+              </Label>
+              <Stat isRed={filteredUsage.current["resource_cpu"] > filteredUsage.limit["resource_cpu"]}>
+                {filteredUsage.current["resource_cpu"]} / {filteredUsage.limit["resource_cpu"]} vCPU
+              </Stat>
+            </UsageBlock>
+            <UsageBlock isRed={filteredUsage.current["resource_memory"] > filteredUsage.limit["resource_memory"]}>
+              <Label isRed={filteredUsage.current["resource_memory"] > filteredUsage.limit["resource_memory"]}>
+                Memory Usage
+              </Label>
+              <Stat isRed={filteredUsage.current["resource_memory"] > filteredUsage.limit["resource_memory"]}>
+                {filteredUsage.current["resource_memory"]/1000} / {filteredUsage.limit["resource_memory"]/1000} GB
+              </Stat>
+            </UsageBlock>
+            <UsageBlock isRed={filteredUsage.current["users"] > filteredUsage.limit["users"]}>
+              <Label isRed={filteredUsage.current["users"] > filteredUsage.limit["users"]}>
+                Users
+              </Label>
+              <Stat isRed={filteredUsage.current["users"] > filteredUsage.limit["users"]}>
+                {filteredUsage.current["users"]} / {filteredUsage.limit["users"]} seats
+              </Stat>
+            </UsageBlock>
+            <UsageBlock isRed={filteredUsage.current["clusters"] > filteredUsage.limit["clusters"]}>
+              <Label isRed={filteredUsage.current["clusters"] > filteredUsage.limit["clusters"]}>
+                Clusters
+              </Label> 
+              <Stat isRed={filteredUsage.current["clusters"] > filteredUsage.limit["clusters"]}>
+                {filteredUsage.current["clusters"]} / {filteredUsage.limit["clusters"]} clusters
+              </Stat>
+            </UsageBlock>
+          </UsageSection>
+        )
+      }
+      <Helper>
+        You have <b>7 days</b> to resolve this issue before your access to the dashboard is restricted.
+      </Helper>
+      <Helper>
+        Have a question about billing? Email us at <a target="_blank" href="mailto:contact@porter.run">contact@porter.run</a>.
+      </Helper>
       <Button
       <Button
         as={Link}
         as={Link}
         to={{
         to={{
@@ -97,12 +117,40 @@ const UpgradeChartModal: React.FC<{}> = () => {
       >
       >
         Take me to billing
         Take me to billing
       </Button>
       </Button>
-    </StyledUpgradeChartModal>
+    </>
   );
   );
 };
 };
 
 
 export default UpgradeChartModal;
 export default UpgradeChartModal;
 
 
+const UsageBlock = styled.div<{ isRed?: boolean }>`
+  border: 1px solid ${props => props.isRed ? "#ff385d" : "#ffffff55"};
+  border-radius: 5px;
+  padding: 18px;
+`;
+
+const Helper = styled.div`
+  color: #aaaabb;
+  margin-top: 12px;
+`;
+
+const Label = styled.div<{ isRed?: boolean }>`
+  margin-bottom: 10px;
+  font-weight: 500;
+  color: ${props => props.isRed ? "#ff385d" : "#ffffff55"};
+`;
+
+const Stat = styled.div<{ isRed?: boolean }>`
+  font-size: 20px;
+  margin-bottom: 25px;
+  color: ${props => props.isRed ? "#ff385d" : "#ffffff55"};
+`;
+
+const Br = styled.div`
+  width: 100%;
+  height: 5px;
+`;
+
 const Button = styled.button`
 const Button = styled.button`
   height: 35px;
   height: 35px;
   background: #616feecc;
   background: #616feecc;
@@ -127,59 +175,11 @@ const Button = styled.button`
   bottom: 20px;
   bottom: 20px;
 `;
 `;
 
 
-const DescriptionSection = styled.div`
+const UsageSection = styled.div`
   margin-top: 10px;
   margin-top: 10px;
-  margin-bottom: 10px;
-`;
-
-const ModalTitle = styled.div`
-  margin: 0px 0px 13px;
-  display: flex;
-  flex: 1;
-  font-family: Work Sans, sans-serif;
-  font-size: 24px;
-  color: #ffffff;
-  user-select: none;
-  font-weight: 700;
-  align-items: center;
-  position: relative;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-`;
-
-const CloseButton = styled.div`
-  position: absolute;
-  display: block;
-  width: 40px;
-  height: 40px;
-  padding: 13px 0 12px 0;
-  z-index: 1;
-  text-align: center;
-  border-radius: 50%;
-  right: 15px;
-  top: 12px;
-  cursor: pointer;
-  :hover {
-    background-color: #ffffff11;
-  }
-`;
-
-const CloseButtonImg = styled.img`
-  width: 14px;
-  margin: 0 auto;
-`;
-
-const StyledUpgradeChartModal = styled.div`
-  width: 100%;
-  position: absolute;
-  left: 0;
-  top: 0;
-  height: 100%;
-  padding: 25px 30px;
-  overflow: hidden;
-  border-radius: 6px;
-  background: #202227;
-  font-size: 13px;
-  line-height: 1.8em;
-  font-family: Work Sans, sans-serif;
-`;
+  margin-bottom: 35px;
+  display: grid;
+  grid-column-gap: 25px;
+  grid-row-gap: 25px;
+  grid-template-columns: repeat(2, minmax(200px, 1fr));
+`;

+ 23 - 3
dashboard/src/main/home/project-settings/BillingPage.tsx

@@ -31,9 +31,29 @@ function BillingPage() {
         <PlanSelect
         <PlanSelect
           theme={{
           theme={{
             base: {
             base: {
-              darkMode: "on",
-              primaryColor: "white",
-              fontFamily: "sans-serif",
+              customFont: 'Work Sans',
+              fontFamily: '"Work Sans", sans-serif',
+              darkMode: 'on',
+              colors: {
+                primary: 'rgba(97, 111, 238, 0.8)',
+                secondary: 'rgb(103, 108, 124)',
+                danger: 'rgb(227, 54, 109)',
+                success: 'rgb(56, 168, 138)',
+              },
+            },
+            card: {
+              backgroundColor: 'rgb(38, 40, 47)',
+              boxShadow: 'rgb(0 0 0 / 33%) 0px 4px 15px 0px',
+              borderRadius: '8px',
+              border: '2px solid rgba(158, 180, 255, 0)',
+            },
+            button: {
+              base: {
+                boxShadow: 'rgb(0 0 0 / 19%) 0px 2px 5px 0px',
+                borderRadius: '5px',
+                fontSize: '14px',
+                fontWeight: '500',
+              },
             },
             },
           }}
           }}
         ></PlanSelect>
         ></PlanSelect>

+ 1 - 0
dashboard/src/main/home/provisioner/ProvisionerSettings.tsx

@@ -271,6 +271,7 @@ const Br = styled.div`
 
 
 const StyledProvisionerSettings = styled.div`
 const StyledProvisionerSettings = styled.div`
   position: relative;
   position: relative;
+  z-index: 0;
 `;
 `;
 
 
 const PositionWrapper = styled.div<{ selectedProvider: string | null }>``;
 const PositionWrapper = styled.div<{ selectedProvider: string | null }>``;

+ 7 - 10
dashboard/src/shared/api.tsx

@@ -763,15 +763,12 @@ const getMetadata = baseApi<{}, {}>("GET", () => {
   return `/api/metadata`;
   return `/api/metadata`;
 });
 });
 
 
-const getWelcome = baseApi<
-  {
-    email: string;
-    isCompany: boolean;
-    company: string;
-    role: string;
-  },
-  {}
->("GET", () => {
+const postWelcome = baseApi<{
+  email: string;
+  isCompany: boolean;
+  company: string;
+  role: string;
+}>("POST", () => {
   return `/api/welcome`;
   return `/api/welcome`;
 });
 });
 
 
@@ -1096,7 +1093,7 @@ export default {
   getBranchContents,
   getBranchContents,
   getBranches,
   getBranches,
   getMetadata,
   getMetadata,
-  getWelcome,
+  postWelcome,
   getChart,
   getChart,
   getCharts,
   getCharts,
   getChartComponents,
   getChartComponents,

+ 2 - 4
dashboard/src/shared/baseApi.tsx

@@ -19,11 +19,9 @@ export const baseApi = <T extends {}, S = {}>(
 
 
     // Handle request type (can refactor)
     // Handle request type (can refactor)
     if (requestType === "POST") {
     if (requestType === "POST") {
-      return axios.post(endpointString, params, {
-      });
+      return axios.post(endpointString, params, {});
     } else if (requestType === "PUT") {
     } else if (requestType === "PUT") {
-      return axios.put(endpointString, params, {
-      });
+      return axios.put(endpointString, params, {});
     } else if (requestType === "DELETE") {
     } else if (requestType === "DELETE") {
       return axios.delete(
       return axios.delete(
         endpointString + "?" + qs.stringify(params, { arrayFormat: "repeat" })
         endpointString + "?" + qs.stringify(params, { arrayFormat: "repeat" })

+ 1 - 1
docs/guides/using-env-groups.md

@@ -20,7 +20,7 @@ You will be redirected to the list of environment groups, and your new environme
 
 
 ![Load env group](https://files.readme.io/c909d6a-env-groups-4.png "env-groups-4.png")
 ![Load env group](https://files.readme.io/c909d6a-env-groups-4.png "env-groups-4.png")
 
 
-You can then select your environment group and click "Load Selected Env Group", which will automatically populate the environment group variables that you previously set. You can modify these environment variables in this tab, for example if you'd like to add environment variables that aren't currently in the environment group. To view all deployment options, head over to our [application deployment docs](https://docs.getporter.dev/docs/add-ons). 
+You can then select your environment group and click "Load Selected Env Group", which will automatically populate the environment group variables that you previously set. You can modify these environment variables in this tab, for example if you'd like to add environment variables that aren't currently in the environment group. To view all deployment options, head over to our [application deployment docs](https://docs.porter.run/docs/addons). 
 
 
 # 🔒 Creating secret environment variables
 # 🔒 Creating secret environment variables