Переглянути джерело

switch from webpack to vite (#4666)

ianedwards 2 роки тому
батько
коміт
4693bb856a
35 змінених файлів з 1392 додано та 1292 видалено
  1. 171 0
      dashboard/index.html
  2. 690 216
      dashboard/package-lock.json
  3. 5 5
      dashboard/package.json
  4. 0 1
      dashboard/src/components/AzureProvisionerSettings.tsx
  5. 249 147
      dashboard/src/components/CloudFormationForm.tsx
  6. 0 159
      dashboard/src/hosted.index.html
  7. 0 61
      dashboard/src/index.html
  8. 11 4
      dashboard/src/index.tsx
  9. 0 60
      dashboard/src/legacy/App.tsx
  10. 0 1
      dashboard/src/legacy/components/AzureProvisionerSettings.tsx
  11. 48 14
      dashboard/src/legacy/components/CloudFormationForm.tsx
  12. 0 159
      dashboard/src/legacy/hosted.index.html
  13. 0 61
      dashboard/src/legacy/index.html
  14. 0 24
      dashboard/src/legacy/index.tsx
  15. 9 10
      dashboard/src/legacy/lib/addons/template.ts
  16. 1 1
      dashboard/src/legacy/main/MainWrapper.tsx
  17. 5 3
      dashboard/src/legacy/main/home/app-dashboard/validate-apply/helm/HelmLatestValues.tsx
  18. 2 2
      dashboard/src/legacy/main/home/compliance-dashboard/ComplianceContext.tsx
  19. 2 2
      dashboard/src/legacy/main/home/infrastructure-dashboard/forms/aws/GrantAWSPermissions.tsx
  20. 24 10
      dashboard/src/legacy/main/home/new-project/WelcomeForm.tsx
  21. 4 8
      dashboard/src/legacy/shared/error_handling/PorterErrorBoundary.tsx
  22. 2 2
      dashboard/src/legacy/shared/error_handling/sentry/setup.ts
  23. 4 3
      dashboard/src/legacy/shared/error_handling/window_error_handling.ts
  24. 1 1
      dashboard/src/main/MainWrapper.tsx
  25. 75 77
      dashboard/src/main/home/app-dashboard/validate-apply/helm/HelmLatestValues.tsx
  26. 2 2
      dashboard/src/main/home/compliance-dashboard/ComplianceContext.tsx
  27. 2 2
      dashboard/src/main/home/infrastructure-dashboard/forms/aws/GrantAWSPermissions.tsx
  28. 24 10
      dashboard/src/main/home/new-project/WelcomeForm.tsx
  29. 1 1
      dashboard/src/shared/auth/ory.ts
  30. 8 10
      dashboard/src/shared/error_handling/PorterErrorBoundary.tsx
  31. 2 2
      dashboard/src/shared/error_handling/sentry/setup.ts
  32. 4 3
      dashboard/src/shared/error_handling/window_error_handling.ts
  33. 13 4
      dashboard/tsconfig.json
  34. 33 0
      dashboard/vite.config.ts
  35. 0 227
      dashboard/webpack.config.js

+ 171 - 0
dashboard/index.html

@@ -0,0 +1,171 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <script>
+      window.dataLayer = window.dataLayer || [];
+    </script>
+    <!-- Google Tag Manager -->
+    <script>
+      (function (w, d, s, l, i) {
+        w[l] = w[l] || [];
+        w[l].push({ "gtm.start": new Date().getTime(), event: "gtm.js" });
+        var f = d.getElementsByTagName(s)[0],
+          j = d.createElement(s),
+          dl = l != "dataLayer" ? "&l=" + l : "";
+        j.async = true;
+        j.src = "https://www.googletagmanager.com/gtm.js?id=" + i + dl;
+        f.parentNode.insertBefore(j, f);
+      })(window, document, "script", "dataLayer", "GTM-P8D92VJ");
+    </script>
+    <!-- End Google Tag Manager -->
+
+    <title>Porter | Dashboard</title>
+    <link rel="icon" href="https://i.ibb.co/HnSk02f/ptr.png" />
+    <meta
+      name="description"
+      content="Kubernetes powered PaaS that runs in your own cloud."
+    />
+    <meta property="og:title" content="Porter" />
+    <meta
+      property="og:image"
+      content="https://i.ibb.co/52g2g7C/porter-wide.png"
+    />
+    <meta
+      property="og:description"
+      content="Kubernetes powered PaaS that runs in your own cloud."
+    />
+    <meta property="og:url" content="https://porter.run" />
+    <link
+      href="https://fonts.googleapis.com/css?family=Work+Sans:400,500,600"
+      rel="stylesheet"
+    />
+    <link href="https://fonts.cdnfonts.com/css/general-sans" rel="stylesheet" />
+    <link
+      href="//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0/katex.min.css"
+      rel="stylesheet"
+    />
+    <link
+      href="https://fonts.googleapis.com/icon?family=Material+Icons|Material+Icons+Outlined|Material+Icons+Round"
+      rel="stylesheet"
+    />
+    <!-- Coding languages icons -->
+    <link
+      rel="stylesheet"
+      href="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.14.0/devicon.min.css"
+    />
+
+    <script>
+      !(function (t, e) {
+        var o, n, p, r;
+        e.__SV ||
+          ((window.posthog = e),
+          (e._i = []),
+          (e.init = function (i, s, a) {
+            function g(t, e) {
+              var o = e.split(".");
+              2 == o.length && ((t = t[o[0]]), (e = o[1])),
+                (t[e] = function () {
+                  t.push([e].concat(Array.prototype.slice.call(arguments, 0)));
+                });
+            }
+            ((p = t.createElement("script")).type = "text/javascript"),
+              (p.async = !0),
+              (p.src = s.api_host + "/static/array.js"),
+              (r = t.getElementsByTagName("script")[0]).parentNode.insertBefore(
+                p,
+                r
+              );
+            var u = e;
+            for (
+              void 0 !== a ? (u = e[a] = []) : (a = "posthog"),
+                u.people = u.people || [],
+                u.toString = function (t) {
+                  var e = "posthog";
+                  return (
+                    "posthog" !== a && (e += "." + a), t || (e += " (stub)"), e
+                  );
+                },
+                u.people.toString = function () {
+                  return u.toString(1) + ".people (stub)";
+                },
+                o =
+                  "capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getFeatureFlag getFeatureFlagPayload reloadFeatureFlags group updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures getActiveMatchingSurveys getSurveys".split(
+                    " "
+                  ),
+                n = 0;
+              n < o.length;
+              n++
+            )
+              g(u, o[n]);
+            e._i.push([i, s, a]);
+          }),
+          (e.__SV = 1));
+      })(document, window.posthog || []);
+      posthog.init("phc_Bna7PjZKfVnkjiDOHx6gUIuIbvWv4M8zsqxYxuRYVo4", {
+        api_host: "https://app.posthog.com",
+      });
+    </script>
+  </head>
+
+  <body>
+    <!-- Google Tag Manager (noscript) -->
+    <noscript
+      ><iframe
+        src="https://www.googletagmanager.com/ns.html?id=GTM-P8D92VJ"
+        height="0"
+        width="0"
+        style="display: none; visibility: hidden"
+      ></iframe
+    ></noscript>
+    <!-- End Google Tag Manager (noscript) -->
+
+    <div id="output"></div>
+    <div id="modal-root"></div>
+    <script type="module" src="./src/index.tsx"></script>
+
+    <script>
+      window.intercomSettings = {
+        api_base: "https://api-iam.intercom.io",
+        app_id: "gq56g49i",
+        alignment: "right",
+      };
+    </script>
+
+    <script>
+      // We pre-filled your app ID in the widget URL: 'https://widget.intercom.io/widget/gq56g49i'
+      (function () {
+        var w = window;
+        var ic = w.Intercom;
+        if (typeof ic === "function") {
+          ic("reattach_activator");
+          ic("update", w.intercomSettings);
+        } else {
+          var d = document;
+          var i = function () {
+            i.c(arguments);
+          };
+          i.q = [];
+          i.c = function (args) {
+            i.q.push(args);
+          };
+          w.Intercom = i;
+          var l = function () {
+            var s = d.createElement("script");
+            s.type = "text/javascript";
+            s.async = true;
+            s.src = "https://widget.intercom.io/widget/gq56g49i";
+            var x = d.getElementsByTagName("script")[0];
+            x.parentNode.insertBefore(s, x);
+          };
+          if (document.readyState === "complete") {
+            l();
+          } else if (w.attachEvent) {
+            w.attachEvent("onload", l);
+          } else {
+            w.addEventListener("load", l, false);
+          }
+        }
+      })();
+    </script>
+  </body>
+</html>

Різницю між файлами не показано, бо вона завелика
+ 690 - 216
dashboard/package-lock.json


+ 5 - 5
dashboard/package.json

@@ -30,6 +30,7 @@
     "@visx/shape": "^3.3.0",
     "@visx/tooltip": "^3.3.0",
     "@visx/xychart": "^3.3.0",
+    "@vitejs/plugin-react": "^4.2.1",
     "ace-builds": "^1.16.0",
     "anser": "^2.0.1",
     "axios": "^0.21.2",
@@ -85,6 +86,8 @@
     "ts-pattern": "^5.0.5",
     "uuid": "^9.0.0",
     "valtio": "^1.2.4",
+    "vite": "^5.2.11",
+    "vite-plugin-node-polyfills": "^0.22.0",
     "zod": "^3.20.2"
   },
   "engines": {
@@ -93,9 +96,8 @@
   },
   "scripts": {
     "test": "jest",
-    "start": "./node_modules/webpack-dev-server/bin/webpack-dev-server.js",
-    "build": "NODE_ENV=\"production\" webpack",
-    "build-and-analyze": "ENABLE_ANALYZER=true NODE_ENV=\"production\" ./node_modules/webpack/bin/webpack.js",
+    "start": "NODE_ENV=\"development\" vite",
+    "build": "NODE_ENV=\"production\" vite build",
     "prepare": "cd .. && husky install dashboard/.husky",
     "lint-staged": "lint-staged"
   },
@@ -170,9 +172,7 @@
     "ts-loader": "^8.0.4",
     "type-fest": "^4.3.1",
     "typescript": "^5.2.2",
-    "webpack": "^4.44.2",
     "webpack-bundle-analyzer": "^4.4.2",
-    "webpack-cli": "^3.3.12",
     "webpack-dev-server": "^3.11.0"
   },
   "lint-staged": {

+ 0 - 1
dashboard/src/components/AzureProvisionerSettings.tsx

@@ -9,7 +9,6 @@ import {
   EnumKubernetesKind,
   NodePoolType,
 } from "@porter-dev/api-contracts";
-import { Label } from "@tanstack/react-query-devtools/build/lib/Explorer";
 import { withRouter, type RouteComponentProps } from "react-router";
 import styled from "styled-components";
 

+ 249 - 147
dashboard/src/components/CloudFormationForm.tsx

@@ -1,23 +1,22 @@
-import React, { useState, useContext, useMemo } from "react";
+import React, { useContext, useMemo, useState } from "react";
+import { useQuery } from "@tanstack/react-query";
 import styled from "styled-components";
-import { v4 as uuidv4 } from 'uuid';
+import { v4 as uuidv4 } from "uuid";
 
 import api from "shared/api";
+import { Context } from "shared/Context";
+import theme from "shared/themes/midnight";
 import aws from "assets/aws.png";
 import cloudformationStatus from "assets/cloud-formation-stack-complete.png";
 
-import { Context } from "shared/Context";
-
-import Text from "./porter/Text";
-import Spacer from "./porter/Spacer";
-import Input from "./porter/Input";
 import Button from "./porter/Button";
-import Link from "./porter/Link";
 import Container from "./porter/Container";
-import Step from "./porter/Step";
-import { useQuery } from "@tanstack/react-query";
+import Input from "./porter/Input";
+import Link from "./porter/Link";
 import Modal from "./porter/Modal";
-import theme from "shared/themes/midnight";
+import Spacer from "./porter/Spacer";
+import Step from "./porter/Step";
+import Text from "./porter/Text";
 import VerticalSteps from "./porter/VerticalSteps";
 import PreflightChecks from "./PreflightChecks";
 
@@ -30,40 +29,49 @@ type Props = {
 const CloudFormationForm: React.FC<Props> = ({
   goBack,
   proceed,
-  switchToCredentialFlow
+  switchToCredentialFlow,
 }) => {
   const [AWSAccountID, setAWSAccountID] = useState("");
   const [currentStep, setCurrentStep] = useState<number>(0);
-  const [hasClickedCloudformationButton, setHasClickedCloudformationButton] = useState(false);
+  const [hasClickedCloudformationButton, setHasClickedCloudformationButton] =
+    useState(false);
   const [showNeedHelpModal, setShowNeedHelpModal] = useState(false);
   const [preflightData, setPreflightData] = useState<any>(undefined);
 
   const { currentProject, user } = useContext(Context);
-  const markStepStarted = async (
-    {
-      step,
-      account_id = "",
-      cloudformation_url = "",
-      error_message = "",
-      login_url = "",
-      external_id = "",
-    }:
-      {
-        step: string;
-        account_id?: string;
-        cloudformation_url?: string;
-        error_message?: string;
-        login_url?: string;
-        external_id?: string;
-      }
-  ) => {
+  const markStepStarted = async ({
+    step,
+    account_id = "",
+    cloudformation_url = "",
+    error_message = "",
+    login_url = "",
+    external_id = "",
+  }: {
+    step: string;
+    account_id?: string;
+    cloudformation_url?: string;
+    error_message?: string;
+    login_url?: string;
+    external_id?: string;
+  }) => {
     try {
       if (currentProject == null) {
         return;
       }
-      await api.updateOnboardingStep("<token>", { step, account_id, cloudformation_url, error_message, login_url, external_id }, {
-        project_id: currentProject.id,
-      });
+      await api.updateOnboardingStep(
+        "<token>",
+        {
+          step,
+          account_id,
+          cloudformation_url,
+          error_message,
+          login_url,
+          external_id,
+        },
+        {
+          project_id: currentProject.id,
+        }
+      );
     } catch (err) {
       // console.log(err);
     }
@@ -73,63 +81,65 @@ const CloudFormationForm: React.FC<Props> = ({
     try {
       if (currentProject == null) {
         return false;
-      };
-      let externalId = getExternalId();
-      let targetARN = `arn:aws:iam::${AWSAccountID}:role/porter-manager`
-      await api
-        .createAWSIntegration(
-          "<token>",
-          {
-            aws_target_arn: targetARN,
-            aws_external_id: externalId,
-          },
-          {
-            id: currentProject.id,
-          }
-        );
+      }
+      const externalId = getExternalId();
+      const targetARN = `arn:aws:iam::${AWSAccountID}:role/porter-manager`;
+      await api.createAWSIntegration(
+        "<token>",
+        {
+          aws_target_arn: targetARN,
+          aws_external_id: externalId,
+        },
+        {
+          id: currentProject.id,
+        }
+      );
       setPreflightData({
-        "Msg": {
-          "preflight_checks": {
+        Msg: {
+          preflight_checks: {
             cloudFormation: {},
-          }
-        }
-      })
-      console.log("true")
+          },
+        },
+      });
+      console.log("true");
 
       return true;
-
     } catch (err) {
-      console.log("false")
-      return false
+      console.log("false");
+      return false;
     }
-  }
+  };
 
   const { data: canProceed } = useQuery(
-    ["createAWSIntegration", currentStep, hasClickedCloudformationButton, AWSAccountID],
+    [
+      "createAWSIntegration",
+      currentStep,
+      hasClickedCloudformationButton,
+      AWSAccountID,
+    ],
     async () => {
       if (currentProject == null) {
         return false;
-      };
-      let externalId = getExternalId();
-      let targetARN = `arn:aws:iam::${AWSAccountID}:role/porter-manager`
-      await api
-        .createAWSIntegration(
-          "<token>",
-          {
-            aws_target_arn: targetARN,
-            aws_external_id: externalId,
-          },
-          {
-            id: currentProject.id,
-          }
-        );
+      }
+      const externalId = getExternalId();
+      const targetARN = `arn:aws:iam::${AWSAccountID}:role/porter-manager`;
+      await api.createAWSIntegration(
+        "<token>",
+        {
+          aws_target_arn: targetARN,
+          aws_external_id: externalId,
+        },
+        {
+          id: currentProject.id,
+        }
+      );
       setPreflightData({
-        "Msg": {
-          "preflight_checks": {
+        Msg: {
+          preflight_checks: {
             cloudFormation: {},
-          }
-        }
-      })
+          },
+        },
+      });
       return true;
     },
     {
@@ -143,14 +153,14 @@ const CloudFormationForm: React.FC<Props> = ({
       },
       retryDelay: 5000,
     }
-  )
+  );
 
   const awsAccountIdInputError = useMemo(() => {
     const regex = /^\d{12}$/;
     if (AWSAccountID.trim().length === 0) {
       return undefined;
     } else if (!regex.test(AWSAccountID)) {
-      return 'A valid AWS Account ID must be a 12-digit number.';
+      return "A valid AWS Account ID must be a 12-digit number.";
     }
     return undefined;
   }, [AWSAccountID]);
@@ -167,74 +177,90 @@ const CloudFormationForm: React.FC<Props> = ({
   const handleContinueWithAWSAccountId = async () => {
     const cloudFormationCheck = await checkCloudFormation();
     cloudFormationCheck ? setCurrentStep(3) : setCurrentStep(2);
-    markStepStarted({ step: "aws-account-id-complete", account_id: AWSAccountID });
-  }
+    markStepStarted({
+      step: "aws-account-id-complete",
+      account_id: AWSAccountID,
+    });
+  };
 
   const handleProceedToProvisionStep = () => {
     try {
       if (currentProject != null) {
-        api.inviteAdmin(
-          "<token>",
-          {},
-          { project_id: currentProject.id }
-        );
-      };
+        api.inviteAdmin("<token>", {}, { project_id: currentProject.id });
+      }
     } catch (err) {
       console.log(err);
     }
-    markStepStarted({ step: "aws-create-integration-success", account_id: AWSAccountID })
+    markStepStarted({
+      step: "aws-create-integration-success",
+      account_id: AWSAccountID,
+    });
     proceed(`arn:aws:iam::${AWSAccountID}:role/porter-manager`);
 
     try {
       window.dataLayer?.push({
-        event: 'provision-attempt',
+        event: "provision-attempt",
         data: {
-          cloud: 'aws',
-          email: user?.email
-        }
+          cloud: "aws",
+          email: user?.email,
+        },
       });
     } catch (err) {
       console.log(err);
     }
-  }
+  };
 
   const reportFailedCreateAWSIntegration = () => {
-    markStepStarted({ step: "aws-create-integration-failed", account_id: AWSAccountID, external_id: getExternalId() })
-  }
+    markStepStarted({
+      step: "aws-create-integration-failed",
+      account_id: AWSAccountID,
+      external_id: getExternalId(),
+    });
+  };
 
   const getExternalId = () => {
-    let externalId = localStorage.getItem(AWSAccountID)
+    let externalId = localStorage.getItem(AWSAccountID);
     if (!externalId) {
-      externalId = uuidv4()
+      externalId = uuidv4();
       localStorage.setItem(AWSAccountID, externalId);
     }
 
-    return externalId
-  }
+    return externalId;
+  };
 
   const directToAWSLogin = () => {
     const login_url = `https://signin.aws.amazon.com/console`;
     markStepStarted({ step: "aws-login-redirect-success", login_url });
     window.open(login_url, "_blank");
-  }
+  };
 
   const directToCloudFormation = () => {
-    setCurrentStep(3)
+    setCurrentStep(3);
     const externalId = getExternalId();
-    let trustArn = process.env.TRUST_ARN ? process.env.TRUST_ARN : "arn:aws:iam::108458755588:role/CAPIManagement";
-    let cloudformation_url = `https://console.aws.amazon.com/cloudformation/home?#/stacks/create/review?templateURL=https://porter-role.s3.us-east-2.amazonaws.com/cloudformation-policy.json&stackName=PorterRole&param_ExternalIdParameter=${externalId}&param_TrustArnParameter=${trustArn}`
-    if (currentProject.aws_ack_auth_enabled === true) {
-      cloudformation_url = `https://console.aws.amazon.com/cloudformation/home?#/stacks/create/review?templateURL=https://porter-role.s3.us-east-2.amazonaws.com/cloudformation-access-policy.json&stackName=PorterRole&param_TrustArnParameter=${trustArn}`
+    const trustArn = import.meta.env.TRUST_ARN
+      ? import.meta.env.TRUST_ARN
+      : "arn:aws:iam::108458755588:role/CAPIManagement";
+    let cloudformation_url = `https://console.aws.amazon.com/cloudformation/home?#/stacks/create/review?templateURL=https://porter-role.s3.us-east-2.amazonaws.com/cloudformation-policy.json&stackName=PorterRole&param_ExternalIdParameter=${externalId}&param_TrustArnParameter=${trustArn}`;
+    if (currentProject.aws_ack_auth_enabled) {
+      cloudformation_url = `https://console.aws.amazon.com/cloudformation/home?#/stacks/create/review?templateURL=https://porter-role.s3.us-east-2.amazonaws.com/cloudformation-access-policy.json&stackName=PorterRole&param_TrustArnParameter=${trustArn}`;
     }
-    markStepStarted({ step: "aws-cloudformation-redirect-success", account_id: AWSAccountID, cloudformation_url, external_id: externalId })
-    window.open(cloudformation_url, "_blank")
+    markStepStarted({
+      step: "aws-cloudformation-redirect-success",
+      account_id: AWSAccountID,
+      cloudformation_url,
+      external_id: externalId,
+    });
+    window.open(cloudformation_url, "_blank");
     setHasClickedCloudformationButton(true);
-  }
+  };
 
   const renderContent = () => {
     return (
       <>
-        <Text>Grant Porter permissions to create infrastructure in your AWS account by following 3 simple steps.</Text>
+        <Text>
+          Grant Porter permissions to create infrastructure in your AWS account
+          by following 3 simple steps.
+        </Text>
         <Spacer y={1} />
         <VerticalSteps
           onlyShowCurrentStep={true}
@@ -242,11 +268,11 @@ const CloudFormationForm: React.FC<Props> = ({
           steps={[
             <>
               <Text size={16}>Log in to your AWS account</Text>
-              <Spacer y={.5} />
+              <Spacer y={0.5} />
               <Text color="helper">
                 Return to Porter after successful login.
               </Text>
-              <Spacer y={.5} />
+              <Spacer y={0.5} />
               <AWSButtonContainer>
                 <ButtonImg src={aws} />
                 <Button
@@ -259,17 +285,22 @@ const CloudFormationForm: React.FC<Props> = ({
                 </Button>
               </AWSButtonContainer>
               <Spacer y={1} />
-              <Button onClick={() => setCurrentStep(1)}>
+              <Button
+                onClick={() => {
+                  setCurrentStep(1);
+                }}
+              >
                 Continue
               </Button>
             </>,
             <>
               <Text size={16}>Enter your AWS account ID</Text>
-              <Spacer y={.5} />
+              <Spacer y={0.5} />
               <Text color="helper">
-                Make sure this is the ID of the account you are currently logged into and would like to provision resources in.
+                Make sure this is the ID of the account you are currently logged
+                into and would like to provision resources in.
               </Text>
-              <Spacer y={.5} />
+              <Spacer y={0.5} />
               <Input
                 label={
                   <Flex>
@@ -277,7 +308,10 @@ const CloudFormationForm: React.FC<Props> = ({
                     <i
                       className="material-icons"
                       onClick={() => {
-                        window.open("https://us-east-1.console.aws.amazon.com/billing/home?region=us-east-1#/account", "_blank")
+                        window.open(
+                          "https://us-east-1.console.aws.amazon.com/billing/home?region=us-east-1#/account",
+                          "_blank"
+                        );
                       }}
                     >
                       help_outline
@@ -291,22 +325,39 @@ const CloudFormationForm: React.FC<Props> = ({
               />
               <Spacer y={1} />
               <StepChangeButtonsContainer>
-                <Button onClick={handleContinueWithAWSAccountId} disabled={awsAccountIdInputError != null || AWSAccountID.length === 0}>Continue</Button>
+                <Button
+                  onClick={handleContinueWithAWSAccountId}
+                  disabled={
+                    awsAccountIdInputError != null || AWSAccountID.length === 0
+                  }
+                >
+                  Continue
+                </Button>
                 <Spacer inline x={0.5} />
-                <Button onClick={() => setCurrentStep(0)} color="#222222">Back</Button>
+                <Button
+                  onClick={() => {
+                    setCurrentStep(0);
+                  }}
+                  color="#222222"
+                >
+                  Back
+                </Button>
               </StepChangeButtonsContainer>
             </>,
             <>
               <Text size={16}>Create an AWS CloudFormation stack</Text>
-              <Spacer y={.5} />
+              <Spacer y={0.5} />
               <Text color="helper">
-                This grants Porter permissions to create infrastructure in your account.
+                This grants Porter permissions to create infrastructure in your
+                account.
               </Text>
-              <Spacer y={.5} />
+              <Spacer y={0.5} />
               <Text color="helper">
-                Clicking the button below will take you to the AWS CloudFormation console. Return to Porter after clicking 'Create stack' in the bottom right corner.
+                Clicking the button below will take you to the AWS
+                CloudFormation console. Return to Porter after clicking 'Create
+                stack' in the bottom right corner.
               </Text>
-              <Spacer y={.5} />
+              <Spacer y={0.5} />
               <AWSButtonContainer>
                 <ButtonImg src={aws} />
                 <Button
@@ -315,17 +366,27 @@ const CloudFormationForm: React.FC<Props> = ({
                   color="linear-gradient(180deg, #26292e, #24272c)"
                   withBorder
                   disabled={canProceed || preflightData}
-                  disabledTooltipMessage={"Porter can already access your account!"}
+                  disabledTooltipMessage={
+                    "Porter can already access your account!"
+                  }
                 >
                   Grant permissions
                 </Button>
               </AWSButtonContainer>
               <Spacer y={1} />
               <StepChangeButtonsContainer>
-                <Button onClick={() => setCurrentStep(3)}>Continue</Button>
+                <Button
+                  onClick={() => {
+                    setCurrentStep(3);
+                  }}
+                >
+                  Continue
+                </Button>
                 <Spacer inline x={0.5} />
                 <Button
-                  onClick={() => setCurrentStep(1)}
+                  onClick={() => {
+                    setCurrentStep(1);
+                  }}
                   color="#222222"
                   // status={canProceed ? "success" : hasClickedCloudformationButton ? "loading" : undefined}
                   loadingText={`Checking if Porter can access AWS account ID ${AWSAccountID}...`}
@@ -337,14 +398,25 @@ const CloudFormationForm: React.FC<Props> = ({
             </>,
             <>
               <Text size={16}>Check permissions</Text>
-              <Spacer y={.5} />
+              <Spacer y={0.5} />
               <Text color="helper">
-                Checking if Porter can access AWS account with ID {AWSAccountID}. This can take up to a minute.<Spacer inline width="10px" /><Link hasunderline onClick={() => setShowNeedHelpModal(true)}>
+                Checking if Porter can access AWS account with ID {AWSAccountID}
+                . This can take up to a minute.
+                <Spacer inline width="10px" />
+                <Link
+                  hasunderline
+                  onClick={() => {
+                    setShowNeedHelpModal(true);
+                  }}
+                >
                   Need help?
                 </Link>
               </Text>
               <Spacer y={1} />
-              <PreflightChecks preflightData={preflightData} provider={"DEFAULT"} />
+              <PreflightChecks
+                preflightData={preflightData}
+                provider={"DEFAULT"}
+              />
               <Spacer y={1} />
               <Container row>
                 <Button
@@ -354,21 +426,38 @@ const CloudFormationForm: React.FC<Props> = ({
                   Continue
                 </Button>
                 <Spacer inline x={0.5} />
-                <Button onClick={() => setCurrentStep(2)} color="#222222">Back</Button>
+                <Button
+                  onClick={() => {
+                    setCurrentStep(2);
+                  }}
+                  color="#222222"
+                >
+                  Back
+                </Button>
               </Container>
             </>,
           ]}
         />
-        {showNeedHelpModal &&
-          <Modal closeModal={() => setShowNeedHelpModal(false)} width={"800px"}>
+        {showNeedHelpModal && (
+          <Modal
+            closeModal={() => {
+              setShowNeedHelpModal(false);
+            }}
+            width={"800px"}
+          >
             <Text size={16}>Granting Porter access to AWS</Text>
             <Spacer y={1} />
             <Text color="helper">
-              Porter needs access to your AWS account in order to create infrastructure. You can grant Porter access to AWS by following these steps:
+              Porter needs access to your AWS account in order to create
+              infrastructure. You can grant Porter access to AWS by following
+              these steps:
             </Text>
             <Spacer y={1} />
             <Step number={1}>
-              <Link to="https://aws.amazon.com/resources/create-account/" target="_blank">
+              <Link
+                to="https://aws.amazon.com/resources/create-account/"
+                target="_blank"
+              >
                 Create an AWS account
               </Link>
               <Spacer inline width="5px" />
@@ -378,16 +467,28 @@ const CloudFormationForm: React.FC<Props> = ({
             <Step number={2}>
               Once you are logged in to your AWS account,
               <Spacer inline width="5px" />
-              <Link to="https://console.aws.amazon.com/billing/home?region=us-east-1#/account" target="_blank">
+              <Link
+                to="https://console.aws.amazon.com/billing/home?region=us-east-1#/account"
+                target="_blank"
+              >
                 copy your account ID
-              </Link>.
+              </Link>
+              .
             </Step>
             <Spacer y={1} />
-            <Step number={3}>Fill in your account ID on Porter and select "Grant permissions".</Step>
+            <Step number={3}>
+              Fill in your account ID on Porter and select "Grant permissions".
+            </Step>
             <Spacer y={1} />
-            <Step number={4}>After being redirected to AWS CloudFormation, select "Create stack" on the bottom right.</Step>
+            <Step number={4}>
+              After being redirected to AWS CloudFormation, select "Create
+              stack" on the bottom right.
+            </Step>
             <Spacer y={1} />
-            <Step number={5}>The stack will start to create. Refresh until the stack status has changed from "CREATE_IN_PROGRESS" to "CREATE_COMPLETE":</Step>
+            <Step number={5}>
+              The stack will start to create. Refresh until the stack status has
+              changed from "CREATE_IN_PROGRESS" to "CREATE_COMPLETE":
+            </Step>
             <Spacer y={1} />
             <ImageDiv>
               <img src={cloudformationStatus} height="250px" />
@@ -395,12 +496,15 @@ const CloudFormationForm: React.FC<Props> = ({
             <Spacer y={1} />
             <Step number={6}>Return to Porter and select "Continue".</Step>
             <Spacer y={1} />
-            <Step number={7}>If you continue to see issues, <a href="mailto:support@porter.run">email support.</a></Step>
+            <Step number={7}>
+              If you continue to see issues,{" "}
+              <a href="mailto:support@porter.run">email support.</a>
+            </Step>
           </Modal>
-        }
+        )}
       </>
     );
-  }
+  };
 
   return (
     <>
@@ -411,9 +515,7 @@ const CloudFormationForm: React.FC<Props> = ({
         </BackButton>
         <Spacer x={1} inline />
         <Img src={aws} />
-        <Text size={16}>
-          Grant AWS permissions
-        </Text>
+        <Text size={16}>Grant AWS permissions</Text>
       </Container>
       <Spacer y={1} />
       {renderContent()}
@@ -481,4 +583,4 @@ const BackButton = styled.div`
 const AWSButtonContainer = styled.div`
   display: flex;
   align-items: center;
-  `;
+`;

+ 0 - 159
dashboard/src/hosted.index.html

@@ -1,159 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <title>Porter | Dashboard</title>
-
-    <script>
-      window.intercomSettings = {
-        app_id: "<%= htmlWebpackPlugin.options.intercomAppId %>",
-        custom_launcher_selector: "#intercom_help",
-        alignment: 'right',
-      };
-    </script>
-
-    <script>
-      // We pre-filled your app ID in the widget URL: 'https://widget.intercom.io/widget/gq56g49i'
-      (function () {
-        var w = window;
-        var ic = w.Intercom;
-        if (typeof ic === "function") {
-          ic("reattach_activator");
-          ic("update", w.intercomSettings);
-        } else {
-          var d = document;
-          var i = function () {
-            i.c(arguments);
-          };
-          i.q = [];
-          i.c = function (args) {
-            i.q.push(args);
-          };
-          w.Intercom = i;
-          var l = function () {
-            var s = d.createElement("script");
-            s.type = "text/javascript";
-            s.async = true;
-            s.src = "<%= htmlWebpackPlugin.options.intercomSrc %>";
-            var x = d.getElementsByTagName("script")[0];
-            x.parentNode.insertBefore(s, x);
-          };
-          if (document.readyState === "complete") {
-            l();
-          } else if (w.attachEvent) {
-            w.attachEvent("onload", l);
-          } else {
-            w.addEventListener("load", l, false);
-          }
-        }
-      })();
-    </script>
-
-    <script>
-      !(function () {
-        var analytics = (window.analytics = window.analytics || []);
-        if (!analytics.initialize)
-          if (analytics.invoked)
-            window.console &&
-              console.error &&
-              console.error("Segment snippet included twice.");
-          else {
-            analytics.invoked = !0;
-            analytics.methods = [
-              "trackSubmit",
-              "trackClick",
-              "trackLink",
-              "trackForm",
-              "pageview",
-              "identify",
-              "reset",
-              "group",
-              "track",
-              "ready",
-              "alias",
-              "debug",
-              "page",
-              "once",
-              "off",
-              "on",
-              "addSourceMiddleware",
-              "addIntegrationMiddleware",
-              "setAnonymousId",
-              "addDestinationMiddleware",
-            ];
-            analytics.factory = function (e) {
-              return function () {
-                var t = Array.prototype.slice.call(arguments);
-                t.unshift(e);
-                analytics.push(t);
-                return analytics;
-              };
-            };
-            for (var e = 0; e < analytics.methods.length; e++) {
-              var key = analytics.methods[e];
-              analytics[key] = analytics.factory(key);
-            }
-            analytics.load = function (key, e) {
-              var t = document.createElement("script");
-              t.type = "text/javascript";
-              t.async = !0;
-              t.src =
-                "https://cdn.segment.com/analytics.js/v1/" +
-                key +
-                "/analytics.min.js";
-              var n = document.getElementsByTagName("script")[0];
-              n.parentNode.insertBefore(t, n);
-              analytics._loadOptions = e;
-            };
-            analytics._writeKey =
-              "<%= htmlWebpackPlugin.options.segmentWriteKey %>";
-            analytics.SNIPPET_VERSION = "4.13.2";
-            analytics.load("<%= htmlWebpackPlugin.options.segmentKey %>");
-            analytics.page();
-          }
-      })();
-    </script>
-    <link rel="icon" href="https://i.ibb.co/HnSk02f/ptr.png" />
-    <meta
-      name="description"
-      content="Kubernetes powered PaaS that runs in your own cloud."
-    />
-    <meta property="og:title" content="Porter" />
-    <meta
-      property="og:image"
-      content="https://i.ibb.co/52g2g7C/porter-wide.png"
-    />
-    <meta
-      property="og:description"
-      content="Kubernetes powered PaaS that runs in your own cloud."
-    />
-    <meta property="og:url" content="https://porter.run" />
-    <link
-      href="https://fonts.googleapis.com/css?family=Work+Sans:400,500,600"
-      rel="stylesheet"
-    />
-    <link href="https://fonts.cdnfonts.com/css/general-sans" rel="stylesheet">
-    <link
-      href="//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0/katex.min.css"
-      rel="stylesheet"
-    />
-    <link
-      href="https://fonts.googleapis.com/icon?family=Material+Icons|Material+Icons+Outlined|Material+Icons+Round"
-      rel="stylesheet"
-    />
-    <!-- Coding languages icons -->
-    <link
-      rel="stylesheet"
-      href="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.14.0/devicon.min.css"
-    />
-
-    <script>
-      !function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.async=!0,p.src=s.api_host+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getFeatureFlag getFeatureFlagPayload reloadFeatureFlags group updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures getActiveMatchingSurveys getSurveys".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
-      posthog.init('phc_Bna7PjZKfVnkjiDOHx6gUIuIbvWv4M8zsqxYxuRYVo4',{api_host:'https://app.posthog.com'})
-    </script>
-  </head>
-
-  <body>
-    <div id="output"></div>
-    <div id="modal-root"></div>
-  </body>
-</html>

+ 0 - 61
dashboard/src/index.html

@@ -1,61 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-
-<head>
-  <script>
-    window.dataLayer = window.dataLayer || [];
-  </script>
-  <!-- Google Tag Manager -->
-  <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
-  new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
-  j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
-  'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
-  })(window,document,'script','dataLayer','GTM-P8D92VJ');</script>
-  <!-- End Google Tag Manager -->
-  
-  <title>Porter | Dashboard</title>
-  <link rel="icon" href="https://i.ibb.co/HnSk02f/ptr.png" />
-  <meta name="description" content="Kubernetes powered PaaS that runs in your own cloud." />
-  <meta property="og:title" content="Porter" />
-  <meta property="og:image" content="https://i.ibb.co/52g2g7C/porter-wide.png" />
-  <meta property="og:description" content="Kubernetes powered PaaS that runs in your own cloud." />
-  <meta property="og:url" content="https://porter.run" />
-  <link href="https://fonts.googleapis.com/css?family=Work+Sans:400,500,600" rel="stylesheet" />
-  <link href="https://fonts.cdnfonts.com/css/general-sans" rel="stylesheet">
-  <link href="//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0/katex.min.css" rel="stylesheet" />
-  <link href="https://fonts.googleapis.com/icon?family=Material+Icons|Material+Icons+Outlined|Material+Icons+Round"
-    rel="stylesheet" />
-  <!-- Coding languages icons -->
-  <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.14.0/devicon.min.css" />
-
-  <script>
-    !function (t, e) { var o, n, p, r; e.__SV || (window.posthog = e, e._i = [], e.init = function (i, s, a) { function g(t, e) { var o = e.split("."); 2 == o.length && (t = t[o[0]], e = o[1]), t[e] = function () { t.push([e].concat(Array.prototype.slice.call(arguments, 0))) } } (p = t.createElement("script")).type = "text/javascript", p.async = !0, p.src = s.api_host + "/static/array.js", (r = t.getElementsByTagName("script")[0]).parentNode.insertBefore(p, r); var u = e; for (void 0 !== a ? u = e[a] = [] : a = "posthog", u.people = u.people || [], u.toString = function (t) { var e = "posthog"; return "posthog" !== a && (e += "." + a), t || (e += " (stub)"), e }, u.people.toString = function () { return u.toString(1) + ".people (stub)" }, o = "capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getFeatureFlag getFeatureFlagPayload reloadFeatureFlags group updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures getActiveMatchingSurveys getSurveys".split(" "), n = 0; n < o.length; n++)g(u, o[n]); e._i.push([i, s, a]) }, e.__SV = 1) }(document, window.posthog || []);
-    posthog.init('phc_Bna7PjZKfVnkjiDOHx6gUIuIbvWv4M8zsqxYxuRYVo4', { api_host: 'https://app.posthog.com' })
-  </script>
-</head>
-
-<body>
-  <!-- Google Tag Manager (noscript) -->
-  <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-P8D92VJ"
-  height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
-  <!-- End Google Tag Manager (noscript) -->
-  
-  <div id="output"></div>
-  <div id="modal-root"></div>
-
-  <script>
-    window.intercomSettings = {
-      api_base: "https://api-iam.intercom.io",
-      app_id: "gq56g49i",
-      alignment: 'right',
-    };
-  </script>
-  
-  <script>
-  // We pre-filled your app ID in the widget URL: 'https://widget.intercom.io/widget/gq56g49i'
-  (function(){var w=window;var ic=w.Intercom;if(typeof ic==="function"){ic('reattach_activator');ic('update',w.intercomSettings);}else{var d=document;var i=function(){i.c(arguments);};i.q=[];i.c=function(args){i.q.push(args);};w.Intercom=i;var l=function(){var s=d.createElement('script');s.type='text/javascript';s.async=true;s.src='https://widget.intercom.io/widget/gq56g49i';var x=d.getElementsByTagName('script')[0];x.parentNode.insertBefore(s,x);};if(document.readyState==='complete'){l();}else if(w.attachEvent){w.attachEvent('onload',l);}else{w.addEventListener('load',l,false);}}})();
-  </script>
-
-</body>
-
-</html>

+ 11 - 4
dashboard/src/index.tsx

@@ -2,11 +2,13 @@ import "core-js/stable";
 import "regenerator-runtime/runtime";
 
 import * as React from "react";
-import * as ReactDOM from "react-dom";
-import App from "./App";
+import ReactDOM from "react-dom/client";
+
 import { SetupSentry } from "shared/error_handling/sentry/setup";
 import { EnableErrorHandling } from "shared/error_handling/window_error_handling";
 
+import App from "./App";
+
 declare global {
   interface Window {
     analytics: any;
@@ -14,10 +16,15 @@ declare global {
   }
 }
 
-if (process.env.ENABLE_SENTRY) {
+if (import.meta.env.ENABLE_SENTRY) {
   SetupSentry();
 }
 
 EnableErrorHandling();
 
-ReactDOM.render(<App />, document.getElementById("output"));
+// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ReactDOM.createRoot(document.getElementById("output")!).render(
+  <React.StrictMode>
+    <App />
+  </React.StrictMode>
+);

+ 0 - 60
dashboard/src/legacy/App.tsx

@@ -1,60 +0,0 @@
-import React, { Component } from "react";
-import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
-import PorterErrorBoundary from "legacy/shared/error_handling/PorterErrorBoundary";
-import midnight from "legacy/shared/themes/midnight";
-import standard from "legacy/shared/themes/standard";
-import { BrowserRouter } from "react-router-dom";
-import styled, { createGlobalStyle, ThemeProvider } from "styled-components";
-
-import MainWrapper from "./main/MainWrapper";
-
-const queryClient = new QueryClient();
-
-export default class App extends Component {
-  render() {
-    return (
-      <QueryClientProvider client={queryClient}>
-        <ThemeProvider theme={standard}>
-          <StyledMain>
-            <GlobalStyle />
-            <PorterErrorBoundary errorBoundaryLocation="globalErrorBoundary">
-              <BrowserRouter>
-                <MainWrapper />
-              </BrowserRouter>
-            </PorterErrorBoundary>
-          </StyledMain>
-        </ThemeProvider>
-      </QueryClientProvider>
-    );
-  }
-}
-
-const GlobalStyle = createGlobalStyle`
-  * {
-    box-sizing: border-box;
-    font-family: 'Work Sans', sans-serif;
-    color-scheme: dark;
-  }
-  
-  body {
-    overscroll-behavior-x: none;
-  }
-
-  a {
-    color: #949eff;
-    text-decoration: none;
-  }
-
-  img {
-    max-width: 100%;
-  }
-`;
-
-const StyledMain = styled.div`
-  height: 100vh;
-  width: 100vw;
-  position: fixed;
-  top: 0;
-  left: 0;
-  color: white;
-`;

+ 0 - 1
dashboard/src/legacy/components/AzureProvisionerSettings.tsx

@@ -9,7 +9,6 @@ import {
   EnumKubernetesKind,
   NodePoolType,
 } from "@porter-dev/api-contracts";
-import { Label } from "@tanstack/react-query-devtools/build/lib/Explorer";
 import dotVertical from "legacy/assets/dot-vertical.svg";
 import Heading from "legacy/components/form-components/Heading";
 import SelectRow from "legacy/components/form-components/SelectRow";

+ 48 - 14
dashboard/src/legacy/components/CloudFormationForm.tsx

@@ -82,8 +82,8 @@ const CloudFormationForm: React.FC<Props> = ({
       if (currentProject == null) {
         return false;
       }
-      let externalId = getExternalId();
-      let targetARN = `arn:aws:iam::${AWSAccountID}:role/porter-manager`;
+      const externalId = getExternalId();
+      const targetARN = `arn:aws:iam::${AWSAccountID}:role/porter-manager`;
       await api.createAWSIntegration(
         "<token>",
         {
@@ -121,8 +121,8 @@ const CloudFormationForm: React.FC<Props> = ({
       if (currentProject == null) {
         return false;
       }
-      let externalId = getExternalId();
-      let targetARN = `arn:aws:iam::${AWSAccountID}:role/porter-manager`;
+      const externalId = getExternalId();
+      const targetARN = `arn:aws:iam::${AWSAccountID}:role/porter-manager`;
       await api.createAWSIntegration(
         "<token>",
         {
@@ -237,11 +237,11 @@ const CloudFormationForm: React.FC<Props> = ({
   const directToCloudFormation = () => {
     setCurrentStep(3);
     const externalId = getExternalId();
-    let trustArn = process.env.TRUST_ARN
-      ? process.env.TRUST_ARN
+    const trustArn = import.meta.env.TRUST_ARN
+      ? import.meta.env.TRUST_ARN
       : "arn:aws:iam::108458755588:role/CAPIManagement";
     let cloudformation_url = `https://console.aws.amazon.com/cloudformation/home?#/stacks/create/review?templateURL=https://porter-role.s3.us-east-2.amazonaws.com/cloudformation-policy.json&stackName=PorterRole&param_ExternalIdParameter=${externalId}&param_TrustArnParameter=${trustArn}`;
-    if (currentProject.aws_ack_auth_enabled === true) {
+    if (currentProject.aws_ack_auth_enabled) {
       cloudformation_url = `https://console.aws.amazon.com/cloudformation/home?#/stacks/create/review?templateURL=https://porter-role.s3.us-east-2.amazonaws.com/cloudformation-access-policy.json&stackName=PorterRole&param_TrustArnParameter=${trustArn}`;
     }
     markStepStarted({
@@ -285,7 +285,13 @@ const CloudFormationForm: React.FC<Props> = ({
                 </Button>
               </AWSButtonContainer>
               <Spacer y={1} />
-              <Button onClick={() => setCurrentStep(1)}>Continue</Button>
+              <Button
+                onClick={() => {
+                  setCurrentStep(1);
+                }}
+              >
+                Continue
+              </Button>
             </>,
             <>
               <Text size={16}>Enter your AWS account ID</Text>
@@ -328,7 +334,12 @@ const CloudFormationForm: React.FC<Props> = ({
                   Continue
                 </Button>
                 <Spacer inline x={0.5} />
-                <Button onClick={() => setCurrentStep(0)} color="#222222">
+                <Button
+                  onClick={() => {
+                    setCurrentStep(0);
+                  }}
+                  color="#222222"
+                >
                   Back
                 </Button>
               </StepChangeButtonsContainer>
@@ -364,10 +375,18 @@ const CloudFormationForm: React.FC<Props> = ({
               </AWSButtonContainer>
               <Spacer y={1} />
               <StepChangeButtonsContainer>
-                <Button onClick={() => setCurrentStep(3)}>Continue</Button>
+                <Button
+                  onClick={() => {
+                    setCurrentStep(3);
+                  }}
+                >
+                  Continue
+                </Button>
                 <Spacer inline x={0.5} />
                 <Button
-                  onClick={() => setCurrentStep(1)}
+                  onClick={() => {
+                    setCurrentStep(1);
+                  }}
                   color="#222222"
                   // status={canProceed ? "success" : hasClickedCloudformationButton ? "loading" : undefined}
                   loadingText={`Checking if Porter can access AWS account ID ${AWSAccountID}...`}
@@ -384,7 +403,12 @@ const CloudFormationForm: React.FC<Props> = ({
                 Checking if Porter can access AWS account with ID {AWSAccountID}
                 . This can take up to a minute.
                 <Spacer inline width="10px" />
-                <Link hasunderline onClick={() => setShowNeedHelpModal(true)}>
+                <Link
+                  hasunderline
+                  onClick={() => {
+                    setShowNeedHelpModal(true);
+                  }}
+                >
                   Need help?
                 </Link>
               </Text>
@@ -402,7 +426,12 @@ const CloudFormationForm: React.FC<Props> = ({
                   Continue
                 </Button>
                 <Spacer inline x={0.5} />
-                <Button onClick={() => setCurrentStep(2)} color="#222222">
+                <Button
+                  onClick={() => {
+                    setCurrentStep(2);
+                  }}
+                  color="#222222"
+                >
                   Back
                 </Button>
               </Container>
@@ -410,7 +439,12 @@ const CloudFormationForm: React.FC<Props> = ({
           ]}
         />
         {showNeedHelpModal && (
-          <Modal closeModal={() => setShowNeedHelpModal(false)} width={"800px"}>
+          <Modal
+            closeModal={() => {
+              setShowNeedHelpModal(false);
+            }}
+            width={"800px"}
+          >
             <Text size={16}>Granting Porter access to AWS</Text>
             <Spacer y={1} />
             <Text color="helper">

+ 0 - 159
dashboard/src/legacy/hosted.index.html

@@ -1,159 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <title>Porter | Dashboard</title>
-
-    <script>
-      window.intercomSettings = {
-        app_id: "<%= htmlWebpackPlugin.options.intercomAppId %>",
-        custom_launcher_selector: "#intercom_help",
-        alignment: 'right',
-      };
-    </script>
-
-    <script>
-      // We pre-filled your app ID in the widget URL: 'https://widget.intercom.io/widget/gq56g49i'
-      (function () {
-        var w = window;
-        var ic = w.Intercom;
-        if (typeof ic === "function") {
-          ic("reattach_activator");
-          ic("update", w.intercomSettings);
-        } else {
-          var d = document;
-          var i = function () {
-            i.c(arguments);
-          };
-          i.q = [];
-          i.c = function (args) {
-            i.q.push(args);
-          };
-          w.Intercom = i;
-          var l = function () {
-            var s = d.createElement("script");
-            s.type = "text/javascript";
-            s.async = true;
-            s.src = "<%= htmlWebpackPlugin.options.intercomSrc %>";
-            var x = d.getElementsByTagName("script")[0];
-            x.parentNode.insertBefore(s, x);
-          };
-          if (document.readyState === "complete") {
-            l();
-          } else if (w.attachEvent) {
-            w.attachEvent("onload", l);
-          } else {
-            w.addEventListener("load", l, false);
-          }
-        }
-      })();
-    </script>
-
-    <script>
-      !(function () {
-        var analytics = (window.analytics = window.analytics || []);
-        if (!analytics.initialize)
-          if (analytics.invoked)
-            window.console &&
-              console.error &&
-              console.error("Segment snippet included twice.");
-          else {
-            analytics.invoked = !0;
-            analytics.methods = [
-              "trackSubmit",
-              "trackClick",
-              "trackLink",
-              "trackForm",
-              "pageview",
-              "identify",
-              "reset",
-              "group",
-              "track",
-              "ready",
-              "alias",
-              "debug",
-              "page",
-              "once",
-              "off",
-              "on",
-              "addSourceMiddleware",
-              "addIntegrationMiddleware",
-              "setAnonymousId",
-              "addDestinationMiddleware",
-            ];
-            analytics.factory = function (e) {
-              return function () {
-                var t = Array.prototype.slice.call(arguments);
-                t.unshift(e);
-                analytics.push(t);
-                return analytics;
-              };
-            };
-            for (var e = 0; e < analytics.methods.length; e++) {
-              var key = analytics.methods[e];
-              analytics[key] = analytics.factory(key);
-            }
-            analytics.load = function (key, e) {
-              var t = document.createElement("script");
-              t.type = "text/javascript";
-              t.async = !0;
-              t.src =
-                "https://cdn.segment.com/analytics.js/v1/" +
-                key +
-                "/analytics.min.js";
-              var n = document.getElementsByTagName("script")[0];
-              n.parentNode.insertBefore(t, n);
-              analytics._loadOptions = e;
-            };
-            analytics._writeKey =
-              "<%= htmlWebpackPlugin.options.segmentWriteKey %>";
-            analytics.SNIPPET_VERSION = "4.13.2";
-            analytics.load("<%= htmlWebpackPlugin.options.segmentKey %>");
-            analytics.page();
-          }
-      })();
-    </script>
-    <link rel="icon" href="https://i.ibb.co/HnSk02f/ptr.png" />
-    <meta
-      name="description"
-      content="Kubernetes powered PaaS that runs in your own cloud."
-    />
-    <meta property="og:title" content="Porter" />
-    <meta
-      property="og:image"
-      content="https://i.ibb.co/52g2g7C/porter-wide.png"
-    />
-    <meta
-      property="og:description"
-      content="Kubernetes powered PaaS that runs in your own cloud."
-    />
-    <meta property="og:url" content="https://porter.run" />
-    <link
-      href="https://fonts.googleapis.com/css?family=Work+Sans:400,500,600"
-      rel="stylesheet"
-    />
-    <link href="https://fonts.cdnfonts.com/css/general-sans" rel="stylesheet">
-    <link
-      href="//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0/katex.min.css"
-      rel="stylesheet"
-    />
-    <link
-      href="https://fonts.googleapis.com/icon?family=Material+Icons|Material+Icons+Outlined|Material+Icons+Round"
-      rel="stylesheet"
-    />
-    <!-- Coding languages icons -->
-    <link
-      rel="stylesheet"
-      href="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.14.0/devicon.min.css"
-    />
-
-    <script>
-      !function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.async=!0,p.src=s.api_host+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getFeatureFlag getFeatureFlagPayload reloadFeatureFlags group updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures getActiveMatchingSurveys getSurveys".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
-      posthog.init('phc_Bna7PjZKfVnkjiDOHx6gUIuIbvWv4M8zsqxYxuRYVo4',{api_host:'https://app.posthog.com'})
-    </script>
-  </head>
-
-  <body>
-    <div id="output"></div>
-    <div id="modal-root"></div>
-  </body>
-</html>

+ 0 - 61
dashboard/src/legacy/index.html

@@ -1,61 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-
-<head>
-  <script>
-    window.dataLayer = window.dataLayer || [];
-  </script>
-  <!-- Google Tag Manager -->
-  <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
-  new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
-  j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
-  'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
-  })(window,document,'script','dataLayer','GTM-P8D92VJ');</script>
-  <!-- End Google Tag Manager -->
-  
-  <title>Porter | Dashboard</title>
-  <link rel="icon" href="https://i.ibb.co/HnSk02f/ptr.png" />
-  <meta name="description" content="Kubernetes powered PaaS that runs in your own cloud." />
-  <meta property="og:title" content="Porter" />
-  <meta property="og:image" content="https://i.ibb.co/52g2g7C/porter-wide.png" />
-  <meta property="og:description" content="Kubernetes powered PaaS that runs in your own cloud." />
-  <meta property="og:url" content="https://porter.run" />
-  <link href="https://fonts.googleapis.com/css?family=Work+Sans:400,500,600" rel="stylesheet" />
-  <link href="https://fonts.cdnfonts.com/css/general-sans" rel="stylesheet">
-  <link href="//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0/katex.min.css" rel="stylesheet" />
-  <link href="https://fonts.googleapis.com/icon?family=Material+Icons|Material+Icons+Outlined|Material+Icons+Round"
-    rel="stylesheet" />
-  <!-- Coding languages icons -->
-  <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.14.0/devicon.min.css" />
-
-  <script>
-    !function (t, e) { var o, n, p, r; e.__SV || (window.posthog = e, e._i = [], e.init = function (i, s, a) { function g(t, e) { var o = e.split("."); 2 == o.length && (t = t[o[0]], e = o[1]), t[e] = function () { t.push([e].concat(Array.prototype.slice.call(arguments, 0))) } } (p = t.createElement("script")).type = "text/javascript", p.async = !0, p.src = s.api_host + "/static/array.js", (r = t.getElementsByTagName("script")[0]).parentNode.insertBefore(p, r); var u = e; for (void 0 !== a ? u = e[a] = [] : a = "posthog", u.people = u.people || [], u.toString = function (t) { var e = "posthog"; return "posthog" !== a && (e += "." + a), t || (e += " (stub)"), e }, u.people.toString = function () { return u.toString(1) + ".people (stub)" }, o = "capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getFeatureFlag getFeatureFlagPayload reloadFeatureFlags group updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures getActiveMatchingSurveys getSurveys".split(" "), n = 0; n < o.length; n++)g(u, o[n]); e._i.push([i, s, a]) }, e.__SV = 1) }(document, window.posthog || []);
-    posthog.init('phc_Bna7PjZKfVnkjiDOHx6gUIuIbvWv4M8zsqxYxuRYVo4', { api_host: 'https://app.posthog.com' })
-  </script>
-</head>
-
-<body>
-  <!-- Google Tag Manager (noscript) -->
-  <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-P8D92VJ"
-  height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
-  <!-- End Google Tag Manager (noscript) -->
-  
-  <div id="output"></div>
-  <div id="modal-root"></div>
-
-  <script>
-    window.intercomSettings = {
-      api_base: "https://api-iam.intercom.io",
-      app_id: "gq56g49i",
-      alignment: 'right',
-    };
-  </script>
-  
-  <script>
-  // We pre-filled your app ID in the widget URL: 'https://widget.intercom.io/widget/gq56g49i'
-  (function(){var w=window;var ic=w.Intercom;if(typeof ic==="function"){ic('reattach_activator');ic('update',w.intercomSettings);}else{var d=document;var i=function(){i.c(arguments);};i.q=[];i.c=function(args){i.q.push(args);};w.Intercom=i;var l=function(){var s=d.createElement('script');s.type='text/javascript';s.async=true;s.src='https://widget.intercom.io/widget/gq56g49i';var x=d.getElementsByTagName('script')[0];x.parentNode.insertBefore(s,x);};if(document.readyState==='complete'){l();}else if(w.attachEvent){w.attachEvent('onload',l);}else{w.addEventListener('load',l,false);}}})();
-  </script>
-
-</body>
-
-</html>

+ 0 - 24
dashboard/src/legacy/index.tsx

@@ -1,24 +0,0 @@
-import "core-js/stable";
-import "regenerator-runtime/runtime";
-
-import * as React from "react";
-import { SetupSentry } from "legacy/shared/error_handling/sentry/setup";
-import { EnableErrorHandling } from "legacy/shared/error_handling/window_error_handling";
-import * as ReactDOM from "react-dom";
-
-import App from "./App";
-
-declare global {
-  interface Window {
-    analytics: any;
-    Intercom: any;
-  }
-}
-
-if (process.env.ENABLE_SENTRY) {
-  SetupSentry();
-}
-
-EnableErrorHandling();
-
-ReactDOM.render(<App />, document.getElementById("output"));

+ 9 - 10
dashboard/src/legacy/lib/addons/template.ts

@@ -1,14 +1,13 @@
 import quivr from "legacy/assets/quivr.png";
-
-import Logs from "main/home/add-on-dashboard/common/Logs";
-import Settings from "main/home/add-on-dashboard/common/Settings";
-import DatadogForm from "main/home/add-on-dashboard/datadog/DatadogForm";
-import MetabaseForm from "main/home/add-on-dashboard/metabase/MetabaseForm";
-import MezmoForm from "main/home/add-on-dashboard/mezmo/MezmoForm";
-import NewRelicForm from "main/home/add-on-dashboard/newrelic/NewRelicForm";
-import QuivrForm from "main/home/add-on-dashboard/quivr/QuivrForm";
-import TailscaleForm from "main/home/add-on-dashboard/tailscale/TailscaleForm";
-import TailscaleOverview from "main/home/add-on-dashboard/tailscale/TailscaleOverview";
+import Logs from "legacy/main/home/add-on-dashboard/common/Logs";
+import Settings from "legacy/main/home/add-on-dashboard/common/Settings";
+import DatadogForm from "legacy/main/home/add-on-dashboard/datadog/DatadogForm";
+import MetabaseForm from "legacy/main/home/add-on-dashboard/metabase/MetabaseForm";
+import MezmoForm from "legacy/main/home/add-on-dashboard/mezmo/MezmoForm";
+import NewRelicForm from "legacy/main/home/add-on-dashboard/newrelic/NewRelicForm";
+import QuivrForm from "legacy/main/home/add-on-dashboard/quivr/QuivrForm";
+import TailscaleForm from "legacy/main/home/add-on-dashboard/tailscale/TailscaleForm";
+import TailscaleOverview from "legacy/main/home/add-on-dashboard/tailscale/TailscaleOverview";
 
 import { type ClientAddon, type ClientAddonType } from ".";
 

+ 1 - 1
dashboard/src/legacy/main/MainWrapper.tsx

@@ -9,7 +9,7 @@ import AuthnProvider from "../shared/auth/AuthnContext";
 import { ContextProvider } from "../shared/Context";
 import Main from "./Main";
 // required styles for Ory Elements
-import "@ory/elements/dist/style.css";
+import "@ory/elements/style.css";
 
 type PropsType = RouteComponentProps;
 

+ 5 - 3
dashboard/src/legacy/main/home/app-dashboard/validate-apply/helm/HelmLatestValues.tsx

@@ -1,4 +1,4 @@
-import React, { useEffect, useMemo, useState } from "react";
+import React from "react";
 import { useQuery } from "@tanstack/react-query";
 import yaml from "js-yaml";
 import loading from "legacy/assets/loading.gif";
@@ -8,7 +8,7 @@ import Text from "legacy/components/porter/Text";
 import YamlEditor from "legacy/components/YamlEditor";
 import api from "legacy/shared/api";
 import styled from "styled-components";
-import { match } from "ts-pattern/dist";
+import { match } from "ts-pattern";
 import { z } from "zod";
 
 type PropsType = {
@@ -69,7 +69,9 @@ const HelmLatestValues: React.FunctionComponent<PropsType> = ({
     <>
       <Checkbox
         checked={withDefaults}
-        toggleChecked={() => setWithDefaults(!withDefaults)}
+        toggleChecked={() => {
+          setWithDefaults(!withDefaults);
+        }}
       >
         <Text color="helper">Include default Helm values</Text>
       </Checkbox>

+ 2 - 2
dashboard/src/legacy/main/home/compliance-dashboard/ComplianceContext.tsx

@@ -121,8 +121,8 @@ export const ProjectComplianceProvider: React.FC<
         return;
       }
 
-      const cidrAllowList = process.env.PORTER_CIDRS
-        ? process.env.PORTER_CIDRS.split(",")
+      const cidrAllowList = import.meta.env.PORTER_CIDRS
+        ? import.meta.env.PORTER_CIDRS.split(",")
         : [];
 
       const updatedKindValues = match(latestContractProto.cluster.kindValues)

+ 2 - 2
dashboard/src/legacy/main/home/infrastructure-dashboard/forms/aws/GrantAWSPermissions.tsx

@@ -222,8 +222,8 @@ const GrantAWSPermissions: React.FC<Props> = ({
       // todo: handle error here
     }
 
-    const trustArn = process.env.TRUST_ARN
-      ? process.env.TRUST_ARN
+    const trustArn = import.meta.env.TRUST_ARN
+      ? import.meta.env.TRUST_ARN
       : "arn:aws:iam::108458755588:role/CAPIManagement";
     const cloudFormationUrl = `https://console.aws.amazon.com/cloudformation/home?#/stacks/create/review?templateURL=https://porter-role.s3.us-east-2.amazonaws.com/cloudformation-access-policy.json&stackName=PorterRole&param_TrustArnParameter=${trustArn}`;
     void reportToAnalytics({

+ 24 - 10
dashboard/src/legacy/main/home/new-project/WelcomeForm.tsx

@@ -1,7 +1,7 @@
 import React, { useState } from "react";
 import axios from "axios";
-import styled from "styled-components";
 import { CSSTransition } from "react-transition-group";
+import styled from "styled-components";
 
 const WelcomeForm = (props: any) => {
   const queryParams = new URLSearchParams(window.location.search);
@@ -34,7 +34,7 @@ const WelcomeForm = (props: any) => {
       .then(() => {
         setIsDone(true);
         axios.post(
-          process.env.DISCORD_WEBHOOK_URL,
+          import.meta.env.DISCORD_WEBHOOK_URL,
           {
             username: "Demo Request",
             content: `**${email}** from **${company}** (website: ${companySite})`,
@@ -46,7 +46,7 @@ const WelcomeForm = (props: any) => {
           }
         );
 
-        axios.get(process.env.ZAPIER_WEBHOOK_URL, {
+        axios.get(import.meta.env.ZAPIER_WEBHOOK_URL, {
           params: {
             email,
             isCompany: true,
@@ -55,7 +55,9 @@ const WelcomeForm = (props: any) => {
           },
         });
       })
-      .catch((error) => alert(error));
+      .catch((error) => {
+        alert(error);
+      });
 
     e.preventDefault();
   };
@@ -66,8 +68,12 @@ const WelcomeForm = (props: any) => {
       timeout={500}
       classNames="alert"
       unmountOnExit
-      onEnter={() => setActive(true)}
-      onExited={() => setActive(false)}
+      onEnter={() => {
+        setActive(true);
+      }}
+      onExited={() => {
+        setActive(false);
+      }}
     >
       <StyledWelcomeForm>
         {isDone ? (
@@ -88,7 +94,9 @@ const WelcomeForm = (props: any) => {
               type="email"
               placeholder="ex: sophon@acme.com"
               value={email}
-              onChange={(e) => setEmail(e.target.value)}
+              onChange={(e) => {
+                setEmail(e.target.value);
+              }}
             />
             <SubtitleAlt>
               <Num>2</Num> What is your company name? *
@@ -97,7 +105,9 @@ const WelcomeForm = (props: any) => {
               type="text"
               placeholder="ex: Acme"
               value={company}
-              onChange={(e) => setCompany(e.target.value)}
+              onChange={(e) => {
+                setCompany(e.target.value);
+              }}
             />
             <SubtitleAlt>
               <Num>3</Num> What is your company website? *
@@ -107,7 +117,9 @@ const WelcomeForm = (props: any) => {
               name="website"
               placeholder="ex: https://acme.com"
               value={companySite}
-              onChange={(e) => setCompanySite(e.target.value)}
+              onChange={(e) => {
+                setCompanySite(e.target.value);
+              }}
             />
             <Submit
               type="submit"
@@ -292,6 +304,8 @@ const StyledWelcomeForm = styled.div`
   &.alert-exit-active {
     opacity: 0;
     transform: translateY(-100px);
-    transition: opacity 500ms, transform 1000ms;
+    transition:
+      opacity 500ms,
+      transform 1000ms;
   }
 `;

+ 4 - 8
dashboard/src/legacy/shared/error_handling/PorterErrorBoundary.tsx

@@ -1,6 +1,6 @@
 import React from "react";
 import * as Sentry from "@sentry/react";
-import { Context, Primitive } from "@sentry/types";
+import { type Context, type Primitive } from "@sentry/types";
 import UnexpectedErrorPage from "legacy/components/UnexpectedErrorPage";
 import { ErrorBoundary } from "react-error-boundary";
 import StackTrace from "stacktrace-js";
@@ -13,13 +13,9 @@ export type PorterErrorBoundaryProps<OnResetProps = {}> = {
   // Used in case the boundary shouldn't refresh but instead do other action
   onReset?: (props: OnResetProps) => unknown;
   // Add more tags to sentry errors
-  tags?: {
-    [key: string]: Primitive;
-  };
+  tags?: Record<string, Primitive>;
   // Add more context for sentry errors
-  context?: {
-    [key: string]: Context;
-  };
+  context?: Record<string, Context>;
 };
 
 const PorterErrorBoundary: React.FC<PorterErrorBoundaryProps> = ({
@@ -37,7 +33,7 @@ const PorterErrorBoundary: React.FC<PorterErrorBoundaryProps> = ({
       // Update the error stack with the StackTrace stack (this helps for minified environments)
       err.stack = stackFramesStringify;
 
-      if (process.env.ENABLE_SENTRY) {
+      if (import.meta.env.ENABLE_SENTRY) {
         Sentry.captureException(err, (scope) => {
           scope.setTags({
             error_boundary_location: errorBoundaryLocation,

+ 2 - 2
dashboard/src/legacy/shared/error_handling/sentry/setup.ts

@@ -1,8 +1,8 @@
 import * as Sentry from "@sentry/react";
 import { Integrations } from "@sentry/tracing";
 
-const SENTRY_DSN = process.env.SENTRY_DSN;
-const SENTRY_ENV = process.env.SENTRY_ENV || "development";
+const SENTRY_DSN = import.meta.env.SENTRY_DSN;
+const SENTRY_ENV = import.meta.env.SENTRY_ENV || "development";
 
 export const SetupSentry = () => {
   if (!SENTRY_DSN) {

+ 4 - 3
dashboard/src/legacy/shared/error_handling/window_error_handling.ts

@@ -1,6 +1,7 @@
-import { stackFramesToString } from "./stack_trace_utils";
-import StackTrace from "stacktrace-js";
 import * as Sentry from "@sentry/react";
+import StackTrace from "stacktrace-js";
+
+import { stackFramesToString } from "./stack_trace_utils";
 
 export function EnableErrorHandling() {
   window.onerror = function (msg, file, line, col, err) {
@@ -11,7 +12,7 @@ export function EnableErrorHandling() {
       // Update the error stack with the StackTrace stack (this helps for minified environments)
       err.stack = stackFramesStringify;
 
-      if (process.env.ENABLE_SENTRY) {
+      if (import.meta.env.ENABLE_SENTRY) {
         Sentry.captureException(err, (scope) => {
           scope.setTags({
             error_boundary_location: "window_error_handling",

+ 1 - 1
dashboard/src/main/MainWrapper.tsx

@@ -9,7 +9,7 @@ import AuthnProvider from "../shared/auth/AuthnContext";
 import { ContextProvider } from "../shared/Context";
 import Main from "./Main";
 // required styles for Ory Elements
-import "@ory/elements/dist/style.css";
+import "@ory/elements/style.css";
 
 type PropsType = RouteComponentProps;
 

+ 75 - 77
dashboard/src/main/home/app-dashboard/validate-apply/helm/HelmLatestValues.tsx

@@ -1,16 +1,16 @@
-import React, { useEffect, useMemo, useState } from "react";
-import styled from "styled-components";
-
-import api from "shared/api";
-
+import React from "react";
 import { useQuery } from "@tanstack/react-query";
 import yaml from "js-yaml";
-import YamlEditor from "components/YamlEditor";
+import styled from "styled-components";
+import { match } from "ts-pattern";
+import { z } from "zod";
+
+import Checkbox from "components/porter/Checkbox";
 import Spacer from "components/porter/Spacer";
 import Text from "components/porter/Text";
-import Checkbox from "components/porter/Checkbox";
-import {z} from "zod";
-import {match} from "ts-pattern/dist";
+import YamlEditor from "components/YamlEditor";
+
+import api from "shared/api";
 import loading from "assets/loading.gif";
 
 type PropsType = {
@@ -28,81 +28,80 @@ const HelmLatestValues: React.FunctionComponent<PropsType> = ({
   appId,
   deploymentTargetId,
 }) => {
-
-    const [withDefaults, setWithDefaults] = React.useState<boolean>(false);
-
-    const res = useQuery(
-      [
-        "getAppHelmValues",
-        projectId,
-        clusterId,
-        appName,
-        appId,
-        deploymentTargetId,
-        withDefaults,
-      ],
-      async () => {
-
-          const helmValues = await api.appHelmValues(
-              "<token>",
-              {
-                app_id: appId,
-                deployment_target_id: deploymentTargetId,
-                with_defaults: withDefaults,
-              },
-              {
-                project_id: projectId,
-                cluster_id: clusterId,
-                porter_app_name: appName,
-              }
-          );
-
-          const parsed = await z.object({helm_values: z.string()}).parseAsync(helmValues.data);
-
-          return yaml.dump(JSON.parse(parsed.helm_values));
-      },
-      {
-        enabled: appName !== "",
-        refetchOnWindowFocus: false,
-      }
+  const [withDefaults, setWithDefaults] = React.useState<boolean>(false);
+
+  const res = useQuery(
+    [
+      "getAppHelmValues",
+      projectId,
+      clusterId,
+      appName,
+      appId,
+      deploymentTargetId,
+      withDefaults,
+    ],
+    async () => {
+      const helmValues = await api.appHelmValues(
+        "<token>",
+        {
+          app_id: appId,
+          deployment_target_id: deploymentTargetId,
+          with_defaults: withDefaults,
+        },
+        {
+          project_id: projectId,
+          cluster_id: clusterId,
+          porter_app_name: appName,
+        }
+      );
+
+      const parsed = await z
+        .object({ helm_values: z.string() })
+        .parseAsync(helmValues.data);
+
+      return yaml.dump(JSON.parse(parsed.helm_values));
+    },
+    {
+      enabled: appName !== "",
+      refetchOnWindowFocus: false,
+    }
   );
 
   return (
-      <>
+    <>
       <Checkbox
-          checked={withDefaults}
-          toggleChecked={() => setWithDefaults(!withDefaults)}
+        checked={withDefaults}
+        toggleChecked={() => {
+          setWithDefaults(!withDefaults);
+        }}
       >
-          <Text color="helper">
-              Include default Helm values
-          </Text>
+        <Text color="helper">Include default Helm values</Text>
       </Checkbox>
       <Spacer y={1} />
-          {match(res)
-              .with({ status: "loading" }, () => (
-                  <LoadingPlaceholder>
-                      <StatusWrapper>
-                          <LoadingGif src={loading} revision={false} /> Updating . . .
-                      </StatusWrapper>
-                  </LoadingPlaceholder>
-              ))
-              .with({ status: "success" }, ({ data }) => (
-                  <StyledValuesYaml>
-                      <Wrapper>
-                          <YamlEditor
-                              value={data}
-                              height="calc(100vh - 412px)"
-                              readOnly={true}
-                          />
-                      </Wrapper>
-                      <Spacer y={0.5} />
-                  </StyledValuesYaml>
-              ))
-              .otherwise(() => null)}
-      </>
+      {match(res)
+        .with({ status: "loading" }, () => (
+          <LoadingPlaceholder>
+            <StatusWrapper>
+              <LoadingGif src={loading} revision={false} /> Updating . . .
+            </StatusWrapper>
+          </LoadingPlaceholder>
+        ))
+        .with({ status: "success" }, ({ data }) => (
+          <StyledValuesYaml>
+            <Wrapper>
+              <YamlEditor
+                value={data}
+                height="calc(100vh - 412px)"
+                readOnly={true}
+              />
+            </Wrapper>
+            <Spacer y={0.5} />
+          </StyledValuesYaml>
+        ))
+        .otherwise(() => null)}
+    </>
   );
-
-}
+};
 
 export default HelmLatestValues;
 
@@ -144,7 +143,6 @@ const StatusWrapper = styled.div`
   margin-right: 25px;
 `;
 
-
 const LoadingPlaceholder = styled.div`
   height: 40px;
   display: flex;

+ 2 - 2
dashboard/src/main/home/compliance-dashboard/ComplianceContext.tsx

@@ -123,8 +123,8 @@ export const ProjectComplianceProvider: React.FC<
         return;
       }
 
-      const cidrAllowList = process.env.PORTER_CIDRS
-        ? process.env.PORTER_CIDRS.split(",")
+      const cidrAllowList = import.meta.env.PORTER_CIDRS
+        ? import.meta.env.PORTER_CIDRS.split(",")
         : [];
 
       const updatedKindValues = match(latestContractProto.cluster.kindValues)

+ 2 - 2
dashboard/src/main/home/infrastructure-dashboard/forms/aws/GrantAWSPermissions.tsx

@@ -223,8 +223,8 @@ const GrantAWSPermissions: React.FC<Props> = ({
       // todo: handle error here
     }
 
-    const trustArn = process.env.TRUST_ARN
-      ? process.env.TRUST_ARN
+    const trustArn = import.meta.env.TRUST_ARN
+      ? import.meta.env.TRUST_ARN
       : "arn:aws:iam::108458755588:role/CAPIManagement";
     const cloudFormationUrl = `https://console.aws.amazon.com/cloudformation/home?#/stacks/create/review?templateURL=https://porter-role.s3.us-east-2.amazonaws.com/cloudformation-access-policy.json&stackName=PorterRole&param_TrustArnParameter=${trustArn}`;
     void reportToAnalytics({

+ 24 - 10
dashboard/src/main/home/new-project/WelcomeForm.tsx

@@ -1,7 +1,7 @@
 import React, { useState } from "react";
 import axios from "axios";
-import styled from "styled-components";
 import { CSSTransition } from "react-transition-group";
+import styled from "styled-components";
 
 const WelcomeForm = (props: any) => {
   const queryParams = new URLSearchParams(window.location.search);
@@ -34,7 +34,7 @@ const WelcomeForm = (props: any) => {
       .then(() => {
         setIsDone(true);
         axios.post(
-          process.env.DISCORD_WEBHOOK_URL,
+          import.meta.env.DISCORD_WEBHOOK_URL,
           {
             username: "Demo Request",
             content: `**${email}** from **${company}** (website: ${companySite})`,
@@ -46,7 +46,7 @@ const WelcomeForm = (props: any) => {
           }
         );
 
-        axios.get(process.env.ZAPIER_WEBHOOK_URL, {
+        axios.get(import.meta.env.ZAPIER_WEBHOOK_URL, {
           params: {
             email,
             isCompany: true,
@@ -55,7 +55,9 @@ const WelcomeForm = (props: any) => {
           },
         });
       })
-      .catch((error) => alert(error));
+      .catch((error) => {
+        alert(error);
+      });
 
     e.preventDefault();
   };
@@ -66,8 +68,12 @@ const WelcomeForm = (props: any) => {
       timeout={500}
       classNames="alert"
       unmountOnExit
-      onEnter={() => setActive(true)}
-      onExited={() => setActive(false)}
+      onEnter={() => {
+        setActive(true);
+      }}
+      onExited={() => {
+        setActive(false);
+      }}
     >
       <StyledWelcomeForm>
         {isDone ? (
@@ -88,7 +94,9 @@ const WelcomeForm = (props: any) => {
               type="email"
               placeholder="ex: sophon@acme.com"
               value={email}
-              onChange={(e) => setEmail(e.target.value)}
+              onChange={(e) => {
+                setEmail(e.target.value);
+              }}
             />
             <SubtitleAlt>
               <Num>2</Num> What is your company name? *
@@ -97,7 +105,9 @@ const WelcomeForm = (props: any) => {
               type="text"
               placeholder="ex: Acme"
               value={company}
-              onChange={(e) => setCompany(e.target.value)}
+              onChange={(e) => {
+                setCompany(e.target.value);
+              }}
             />
             <SubtitleAlt>
               <Num>3</Num> What is your company website? *
@@ -107,7 +117,9 @@ const WelcomeForm = (props: any) => {
               name="website"
               placeholder="ex: https://acme.com"
               value={companySite}
-              onChange={(e) => setCompanySite(e.target.value)}
+              onChange={(e) => {
+                setCompanySite(e.target.value);
+              }}
             />
             <Submit
               type="submit"
@@ -292,6 +304,8 @@ const StyledWelcomeForm = styled.div`
   &.alert-exit-active {
     opacity: 0;
     transform: translateY(-100px);
-    transition: opacity 500ms, transform 1000ms;
+    transition:
+      opacity 500ms,
+      transform 1000ms;
   }
 `;

+ 1 - 1
dashboard/src/shared/auth/ory.ts

@@ -5,7 +5,7 @@ import { type AxiosError } from "axios";
 import { useHistory } from "react-router-dom";
 
 export const basePath =
-  process.env.REACT_APP_ORY_URL || "http://localhost:4000";
+  import.meta.env.REACT_APP_ORY_URL || "http://localhost:4000";
 
 export const ory = new FrontendApi(
   new Configuration({

+ 8 - 10
dashboard/src/shared/error_handling/PorterErrorBoundary.tsx

@@ -1,9 +1,11 @@
-import UnexpectedErrorPage from "components/UnexpectedErrorPage";
 import React from "react";
-import { ErrorBoundary } from "react-error-boundary";
 import * as Sentry from "@sentry/react";
+import { type Context, type Primitive } from "@sentry/types";
+import { ErrorBoundary } from "react-error-boundary";
 import StackTrace from "stacktrace-js";
-import { Context, Primitive } from "@sentry/types";
+
+import UnexpectedErrorPage from "components/UnexpectedErrorPage";
+
 import { stackFramesToString } from "./stack_trace_utils";
 
 export type PorterErrorBoundaryProps<OnResetProps = {}> = {
@@ -12,13 +14,9 @@ export type PorterErrorBoundaryProps<OnResetProps = {}> = {
   // Used in case the boundary shouldn't refresh but instead do other action
   onReset?: (props: OnResetProps) => unknown;
   // Add more tags to sentry errors
-  tags?: {
-    [key: string]: Primitive;
-  };
+  tags?: Record<string, Primitive>;
   // Add more context for sentry errors
-  context?: {
-    [key: string]: Context;
-  };
+  context?: Record<string, Context>;
 };
 
 const PorterErrorBoundary: React.FC<PorterErrorBoundaryProps> = ({
@@ -36,7 +34,7 @@ const PorterErrorBoundary: React.FC<PorterErrorBoundaryProps> = ({
       // Update the error stack with the StackTrace stack (this helps for minified environments)
       err.stack = stackFramesStringify;
 
-      if (process.env.ENABLE_SENTRY) {
+      if (import.meta.env.ENABLE_SENTRY) {
         Sentry.captureException(err, (scope) => {
           scope.setTags({
             error_boundary_location: errorBoundaryLocation,

+ 2 - 2
dashboard/src/shared/error_handling/sentry/setup.ts

@@ -1,8 +1,8 @@
 import * as Sentry from "@sentry/react";
 import { Integrations } from "@sentry/tracing";
 
-const SENTRY_DSN = process.env.SENTRY_DSN;
-const SENTRY_ENV = process.env.SENTRY_ENV || "development";
+const SENTRY_DSN = import.meta.env.SENTRY_DSN;
+const SENTRY_ENV = import.meta.env.SENTRY_ENV || "development";
 
 export const SetupSentry = () => {
   if (!SENTRY_DSN) {

+ 4 - 3
dashboard/src/shared/error_handling/window_error_handling.ts

@@ -1,6 +1,7 @@
-import { stackFramesToString } from "./stack_trace_utils";
-import StackTrace from "stacktrace-js";
 import * as Sentry from "@sentry/react";
+import StackTrace from "stacktrace-js";
+
+import { stackFramesToString } from "./stack_trace_utils";
 
 export function EnableErrorHandling() {
   window.onerror = function (msg, file, line, col, err) {
@@ -11,7 +12,7 @@ export function EnableErrorHandling() {
       // Update the error stack with the StackTrace stack (this helps for minified environments)
       err.stack = stackFramesStringify;
 
-      if (process.env.ENABLE_SENTRY) {
+      if (import.meta.env.ENABLE_SENTRY) {
         Sentry.captureException(err, (scope) => {
           scope.setTags({
             error_boundary_location: "window_error_handling",

+ 13 - 4
dashboard/tsconfig.json

@@ -1,13 +1,22 @@
 {
   "compilerOptions": {
     "lib": ["ESNext"],
-    "baseUrl": "src",
+    "baseUrl": ".",
+    "paths": {
+      "assets/*": ["src/assets/*"],
+      "components/*": ["src/components/*"],
+      "legacy/*": ["src/legacy/*"],
+      "lib/*": ["src/lib/*"],
+      "main/*": ["src/main/*"],
+      "utils/*": ["src/utils/*"],
+      "shared/*": ["src/shared/*"]
+    },
     "outDir": "./build/",
     "sourceMap": true,
     "noImplicitAny": true,
-    "module": "es6",
-    "target": "es5",
-    "jsx": "react",
+    "module": "ESNext",
+    "target": "ESNext",
+    "jsx": "react-jsx",
     "allowJs": true,
     "allowSyntheticDefaultImports": true,
     "removeComments": true,

+ 33 - 0
dashboard/vite.config.ts

@@ -0,0 +1,33 @@
+import react from "@vitejs/plugin-react";
+import { defineConfig } from "vite";
+import { nodePolyfills } from "vite-plugin-node-polyfills";
+
+export default defineConfig(({ mode }) => ({
+  plugins: [react(), nodePolyfills()],
+  resolve: {
+    alias: {
+      assets: "/src/assets",
+      components: "/src/components",
+      legacy: "/src/legacy",
+      lib: "/src/lib",
+      main: "/src/main",
+      shared: "/src/shared",
+      utils: "/src/utils",
+    },
+  },
+  build: {
+    outDir: "build",
+  },
+  server: {
+    port: 8081,
+    proxy: {
+      ...(mode === "development" && {
+        "/api": {
+          target: "http://localhost:8080",
+          changeOrigin: true,
+          ws: true,
+        },
+      }),
+    },
+  },
+}));

+ 0 - 227
dashboard/webpack.config.js

@@ -1,227 +0,0 @@
-const path = require("path");
-const HtmlWebpackPlugin = require("html-webpack-plugin");
-const webpack = require("webpack");
-const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin");
-
-const dotenv = require("dotenv");
-
-const BundleAnalyzerPlugin =
-  require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
-
-const TerserPlugin = require("terser-webpack-plugin");
-
-module.exports = () => {
-  let envPath = ".env";
-  if (process.env.ENV_FILE !== "") {
-    envPath = process.env.ENV_FILE;
-  }
-  console.log(`using envfile from path ${envPath}`);
-
-  let env = dotenv.config({ path: envPath }).parsed;
-  console.log(env);
-  if (!env) {
-    env = process.env;
-  }
-
-  const envKeys = Object.keys(env).reduce((prev, next) => {
-    const varName = `process.env.${next}`;
-    if (typeof env[next] !== "string") return prev;
-
-    if (env[next].toLowerCase() === "true") {
-      prev[varName] = true;
-    } else if (env[next].toLowerCase() === "false") {
-      prev[varName] = false;
-    } else {
-      prev[varName] = JSON.stringify(env[next]);
-    }
-
-    return prev;
-  }, {});
-
-  // Check first the env file and if it's empty, check out the node env of the process.
-  let isDevelopment = env.NODE_ENV !== "production";
-  if (process.env.NODE_ENV !== env.NODE_ENV) {
-    isDevelopment = process.env.NODE_ENV !== "production";
-  }
-
-  let htmlPluginOpts = {
-    template: path.resolve(__dirname, "src", "index.html"),
-  };
-
-  if (env.IS_HOSTED) {
-    htmlPluginOpts = {
-      template: path.resolve(__dirname, "src", "hosted.index.html"),
-      intercomAppId: `${env.INTERCOM_APP_ID}`,
-      hotjarId: `${env.HOTJAR_ID}`,
-      intercomSrc: `${process.env.INTERCOM_SRC}`,
-      segmentWriteKey: `${process.env.SEGMENT_WRITE_KEY}`,
-      segmentKey: `${process.env.SEGMENT_PUBLIC_KEY}`,
-    };
-  }
-
-  /**
-   * @type {webpack.Configuration}
-   */
-  const config = {
-    entry: [
-      "core-js/modules/es.promise",
-      "core-js/modules/es.array.iterator",
-      "./src/index.tsx",
-    ],
-    target: "web",
-    mode: isDevelopment ? "development" : "production",
-    devtool: "source-map",
-    module: {
-      rules: [
-        {
-          test: /\.(ts|tsx|mjs|js|jsx)$/,
-          exclude: /node_modules\/(?!(chart.js|react-chartjs-2)\/).*/,
-          use: [
-            {
-              loader: require.resolve("babel-loader"),
-              options: {
-                plugins: [
-                  isDevelopment && require.resolve("react-refresh/babel"),
-                ].filter(Boolean),
-              },
-            },
-          ],
-        },
-        {
-          test: /\.(mjs|js)$/,
-          include: /node_modules/,
-          type: "javascript/auto",
-        },
-        {
-          test: /\.mjs$/,
-          use: [
-            {
-              loader: require.resolve("babel-loader"),
-              options: {
-                plugins: [
-                  isDevelopment && require.resolve("react-refresh/babel"),
-                ].filter(Boolean),
-              },
-            },
-          ],
-        },
-        {
-          enforce: "pre",
-          test: /\.(ts|tsx|mjs|js|jsx)$/,
-          loader: "source-map-loader",
-        },
-        {
-          test: /\.(png|svg|jpg|gif|mp3)$/,
-          use: ["file-loader"],
-        },
-        // {
-        //   test: /\.css$/i,
-        //   loader: "css-loader",
-        //   options: {
-        //     import: true,
-        //   },
-        // },
-        {
-          test: /\.css$/i,
-          use: [
-            { loader: "style-loader", options: { injectType: "linkTag" } },
-            { loader: "file-loader" },
-          ],
-        },
-        {
-          test: /\.(woff(2)?|ttf|eot)(\?v=\d+\.\d+\.\d+)?$/,
-          use: [
-            {
-              loader: "file-loader",
-              options: {
-                name: "[name].[ext]",
-                outputPath: "fonts/",
-              },
-            },
-          ],
-        },
-      ],
-    },
-    resolve: {
-      alias: {
-        legacy: path.resolve(__dirname, "src/legacy/"),
-      },
-      modules: [path.resolve(__dirname, "src"), "node_modules"],
-      extensions: ["*", ".tsx", ".ts", ".js", ".jsx", ".json"],
-    },
-    output: {
-      filename: "bundle.js",
-      path: path.resolve(__dirname, "build"),
-      publicPath: "/",
-    },
-    devServer: {
-      historyApiFallback: true,
-      disableHostCheck: true,
-      host: "0.0.0.0",
-      port: env.DEV_SERVER_PORT || 8080,
-      hot: true,
-    },
-    plugins: [
-      new HtmlWebpackPlugin(htmlPluginOpts),
-      new webpack.DefinePlugin(envKeys),
-      isDevelopment && new ReactRefreshWebpackPlugin(),
-    ].filter(Boolean),
-  };
-
-  if (!isDevelopment) {
-    config.optimization = {
-      minimize: true,
-      minimizer: [
-        new TerserPlugin({
-          test: /\.(ts|tsx|mjs|js|jsx)$/,
-          terserOptions: {
-            parse: {
-              // We want terser to parse ecma 8 code. However, we don't want it
-              // to apply minification steps that turns valid ecma 5 code
-              // into invalid ecma 5 code. This is why the `compress` and `output`
-              ecma: 8,
-            },
-            compress: {
-              ecma: 5,
-              warnings: false,
-              inline: 2,
-            },
-            mangle: {
-              // Find work around for Safari 10+
-              safari10: true,
-            },
-            output: {
-              ecma: 5,
-              comments: false,
-              ascii_only: true,
-            },
-          },
-
-          // Use multi-process parallel running to improve the build speed
-          parallel: true,
-        }),
-      ],
-    };
-  }
-
-  if (env.ENABLE_ANALYZER) {
-    config.plugins.push(new BundleAnalyzerPlugin());
-  }
-  if (env.ENABLE_PROXY) {
-    if (!env.API_SERVER) {
-      throw new Error(
-        "API_SERVER is not present on .env! Please setup the api server url if you want the proxy to work! API_SERVER example: http://localhost:8080"
-      );
-    }
-    config.devServer.proxy = {
-      "/api": {
-        logLevel: "debug",
-        target: env.API_SERVER, // target host
-        changeOrigin: true, // needed for virtual hosted sites
-        ws: true, // proxy websockets
-      },
-    };
-  }
-
-  return config;
-};

Деякі файли не було показано, через те що забагато файлів було змінено