Przeglądaj źródła

delete files with unused exports (#4650)

ianedwards 2 lat temu
rodzic
commit
2d33d4cfe1
79 zmienionych plików z 0 dodań i 12276 usunięć
  1. 0 15
      dashboard/src/components/Boilerplate.tsx
  2. 0 299
      dashboard/src/components/SOC2Checks.tsx
  3. 0 118
      dashboard/src/components/expanded-object/Header.tsx
  4. 0 34
      dashboard/src/components/porter/TemplateComponent.tsx
  5. 0 103
      dashboard/src/components/repo-selector/NewGHAction.tsx
  6. 0 449
      dashboard/src/lib/hooks/useClusterResourceLimits.ts
  7. 0 35
      dashboard/src/lib/hooks/useResizeObserver.ts
  8. 0 301
      dashboard/src/main/home/WelcomeForm.tsx
  9. 0 108
      dashboard/src/main/home/app-dashboard/app-view/tabs/preview-environments/PreviewEnvironmentSettings.tsx
  10. 0 137
      dashboard/src/main/home/app-dashboard/build-settings/AdvancedBuildSettings.tsx
  11. 0 195
      dashboard/src/main/home/app-dashboard/build-settings/BuildSettingsTab.tsx
  12. 0 261
      dashboard/src/main/home/app-dashboard/build-settings/DetectDockerfileAndPorterYaml.tsx
  13. 0 195
      dashboard/src/main/home/app-dashboard/build-settings/SharedBuildSettings.tsx
  14. 0 158
      dashboard/src/main/home/app-dashboard/build-settings/buildpacks/AddCustomBuildpackComponent.tsx
  15. 0 168
      dashboard/src/main/home/app-dashboard/build-settings/buildpacks/BuildpackCard.tsx
  16. 0 145
      dashboard/src/main/home/app-dashboard/build-settings/buildpacks/BuildpackConfigurationModal.tsx
  17. 0 142
      dashboard/src/main/home/app-dashboard/build-settings/buildpacks/BuildpackList.tsx
  18. 0 226
      dashboard/src/main/home/app-dashboard/build-settings/buildpacks/BuildpackSettings.tsx
  19. 0 128
      dashboard/src/main/home/app-dashboard/create-app/PorterYamlModal.tsx
  20. 0 224
      dashboard/src/main/home/app-dashboard/new-app-flow/GithubConnectModal.tsx
  21. 0 97
      dashboard/src/main/home/app-dashboard/new-app-flow/SourceSettings.tsx
  22. 0 144
      dashboard/src/main/home/app-dashboard/new-app-flow/schema.tsx
  23. 0 237
      dashboard/src/main/home/app-dashboard/validate-apply/app-settings/ExpandableEnvGroup.tsx
  24. 0 189
      dashboard/src/main/home/app-dashboard/validate-apply/services-settings/tabs/Old_GPUResources.tsx
  25. 0 181
      dashboard/src/main/home/app-dashboard/validate-apply/services-settings/tabs/utils.ts
  26. 0 14
      dashboard/src/main/home/cluster-dashboard/ClusterPlaceholderContainer.tsx
  27. 0 408
      dashboard/src/main/home/cluster-dashboard/dashboard/Compliance.tsx
  28. 0 135
      dashboard/src/main/home/cluster-dashboard/dashboard/DonutChart.tsx
  29. 0 246
      dashboard/src/main/home/cluster-dashboard/dashboard/PorterAppDashboard.tsx
  30. 0 104
      dashboard/src/main/home/cluster-dashboard/expanded-chart/DeploymentTypeStacks.tsx
  31. 0 115
      dashboard/src/main/home/cluster-dashboard/expanded-chart/deploy/DeploySection.tsx
  32. 0 42
      dashboard/src/main/home/cluster-dashboard/expanded-chart/deploy/EventTab.tsx
  33. 0 135
      dashboard/src/main/home/cluster-dashboard/expanded-chart/jobs/TempJobList.tsx
  34. 0 52
      dashboard/src/main/home/cluster-dashboard/expanded-chart/status/ConnectToLogsInstructionModal.tsx
  35. 0 123
      dashboard/src/main/home/cluster-dashboard/preview-environments/components/RecreateWorkflowFilesModal.tsx
  36. 0 243
      dashboard/src/main/home/cluster-dashboard/preview-environments/deployments/PullRequestCard.tsx
  37. 0 264
      dashboard/src/main/home/dashboard/ClusterOverview.tsx
  38. 0 28
      dashboard/src/main/home/dashboard/PipelinesSection.tsx
  39. 0 56
      dashboard/src/main/home/database-dashboard/DatabaseHeaderItem.tsx
  40. 0 127
      dashboard/src/main/home/database-dashboard/tabs/ConnectedAppsTab.tsx
  41. 0 34
      dashboard/src/main/home/database-dashboard/tabs/MetricsTab.tsx
  42. 0 23
      dashboard/src/main/home/database-dashboard/utils.tsx
  43. 0 30
      dashboard/src/main/home/env-dashboard/EnvGroupList.tsx
  44. 0 243
      dashboard/src/main/home/infrastructure/ExpandedInfra.tsx
  45. 0 337
      dashboard/src/main/home/infrastructure/InfrastructureList.tsx
  46. 0 39
      dashboard/src/main/home/infrastructure/InfrastructureRouter.tsx
  47. 0 342
      dashboard/src/main/home/infrastructure/components/DeployList.tsx
  48. 0 401
      dashboard/src/main/home/infrastructure/components/ExpandedOperation.tsx
  49. 0 120
      dashboard/src/main/home/infrastructure/components/InfraResourceList.tsx
  50. 0 96
      dashboard/src/main/home/infrastructure/components/InfraSettings.tsx
  51. 0 561
      dashboard/src/main/home/infrastructure/components/ProvisionInfra.tsx
  52. 0 167
      dashboard/src/main/home/infrastructure/components/credentials/AWSCredentialForm.tsx
  53. 0 108
      dashboard/src/main/home/infrastructure/components/credentials/AWSCredentialList.tsx
  54. 0 140
      dashboard/src/main/home/infrastructure/components/credentials/AzureCredentialForm.tsx
  55. 0 110
      dashboard/src/main/home/infrastructure/components/credentials/AzureCredentialList.tsx
  56. 0 101
      dashboard/src/main/home/infrastructure/components/credentials/ClusterList.tsx
  57. 0 115
      dashboard/src/main/home/infrastructure/components/credentials/CredentialList.tsx
  58. 0 114
      dashboard/src/main/home/infrastructure/components/credentials/GCPCredentialForm.tsx
  59. 0 108
      dashboard/src/main/home/infrastructure/components/credentials/GCPCredentialList.tsx
  60. 0 105
      dashboard/src/main/home/integrations/edit-integration/DockerHubForm.tsx
  61. 0 139
      dashboard/src/main/home/integrations/edit-integration/ECRForm.tsx
  62. 0 124
      dashboard/src/main/home/integrations/edit-integration/EKSForm.tsx
  63. 0 38
      dashboard/src/main/home/integrations/edit-integration/EditIntegrationForm.tsx
  64. 0 147
      dashboard/src/main/home/integrations/edit-integration/GCRForm.tsx
  65. 0 111
      dashboard/src/main/home/integrations/edit-integration/GKEForm.tsx
  66. 0 240
      dashboard/src/main/home/integrations/edit-integration/GitlabIntegrationList.tsx
  67. 0 13
      dashboard/src/main/home/launch/Boilerplate.tsx
  68. 0 281
      dashboard/src/main/home/navbar/Feedback.tsx
  69. 0 56
      dashboard/src/main/home/project-settings/Bars copy.tsx
  70. 0 99
      dashboard/src/main/home/provisioner/InfraStatuses.tsx
  71. 0 191
      dashboard/src/main/home/provisioner/Provisioner.tsx
  72. 0 273
      dashboard/src/main/home/provisioner/ProvisionerLogs.tsx
  73. 0 140
      dashboard/src/main/home/sidebar/AddCluster/AzureCredentialForm.tsx
  74. 0 110
      dashboard/src/main/home/sidebar/AddCluster/AzureCredentialList.tsx
  75. 0 101
      dashboard/src/main/home/sidebar/AddCluster/ClusterList.tsx
  76. 0 114
      dashboard/src/main/home/sidebar/AddCluster/GCPCredentialForm.tsx
  77. 0 108
      dashboard/src/main/home/sidebar/AddCluster/GCPCredentialList.tsx
  78. 0 294
      dashboard/src/main/home/sidebar/ProjectSection.tsx
  79. 0 22
      dashboard/src/shared/feedback.tsx

+ 0 - 15
dashboard/src/components/Boilerplate.tsx

@@ -1,15 +0,0 @@
-import React, { useState } from "react";
-
-import styled from "styled-components";
-
-type Props = {};
-
-const Boilerplate: React.FC<Props> = (props) => {
-  const [someState, setSomeState] = useState("");
-
-  return <StyledBoilerplate></StyledBoilerplate>;
-};
-
-export default Boilerplate;
-
-const StyledBoilerplate = styled.div``;

+ 0 - 299
dashboard/src/components/SOC2Checks.tsx

@@ -1,299 +0,0 @@
-import React, { useEffect, useState } from "react";
-import { withRouter, type RouteComponentProps } from "react-router";
-import styled from "styled-components";
-
-import Container from "components/porter/Container";
-
-import external_link from "assets/external-link.svg";
-import failure from "assets/failure.svg";
-import pending from "assets/pending.svg";
-import healthy from "assets/status-healthy.png";
-
-import Loading from "./Loading";
-import Link from "./porter/Link";
-import Spacer from "./porter/Spacer";
-import Text from "./porter/Text";
-import ToggleRow from "./porter/ToggleRow";
-import { type Soc2Data, type Soc2Check } from "shared/types";
-
-type Props = RouteComponentProps & {
-  soc2Data: Soc2Check;
-  error?: string;
-  enableAll: boolean;
-  setSoc2Data: (x: Soc2Check) => void;
-  readOnly: boolean;
-};
-type ItemProps = RouteComponentProps & {
-  checkKey: string;
-  checkLabel?: string;
-};
-
-const SOC2Checks: React.FC<Props> = ({
-  soc2Data,
-  enableAll,
-  setSoc2Data,
-  readOnly,
-}) => {
-  // const { soc2Data, setSoc2Data } = useContext(Context);
-  const soc2Checks = soc2Data?.soc2_checks || {};
-
-  const combinedKeys = new Set([...Object.keys(soc2Checks)]);
-
-  useEffect(() => {
-    if (enableAll) {
-      const newSOC2Checks = Object.keys(soc2Checks).reduce((acc, key) => {
-        acc[key] = {
-          ...soc2Checks[key],
-          status: soc2Checks[key].enabled
-            ? soc2Checks[key].status === "PENDING_ENABLED"
-              ? "PENDING_ENABLED"
-              : "ENABLED"
-            : "PENDING_ENABLED",
-        };
-        return acc;
-      }, {});
-      setSoc2Data((prev: Soc2Data) => ({
-        ...prev,
-        soc2_checks: newSOC2Checks,
-      }));
-    } else {
-      const newSOC2Checks = Object.keys(soc2Checks).reduce((acc, key) => {
-        acc[key] = {
-          ...soc2Checks[key],
-          status: !soc2Checks[key].enabled
-            ? ""
-            : soc2Checks[key].status === "PENDING_ENABLED"
-              ? "PENDING_ENABLED"
-              : "ENABLED",
-        };
-        return acc;
-      }, {});
-      setSoc2Data((prev: Soc2Data) => ({
-        ...prev,
-        soc2_checks: newSOC2Checks,
-      }));
-    }
-  }, [enableAll]);
-
-  const Soc2Item: React.FC<ItemProps> = ({ checkKey, checkLabel }) => {
-    const checkData = soc2Data?.soc2_checks?.[checkKey];
-    const hasMessage = checkData?.message;
-    const enabled = checkData?.enabled;
-    const status = checkData?.status;
-
-    const [isExpanded, setIsExpanded] = useState(true);
-
-    const handleToggle = (): void => {
-      if (hasMessage && enabled) {
-        setIsExpanded(!isExpanded);
-      }
-    };
-
-    const determineStatus = (currentStatus: string): string => {
-      if (currentStatus === "ENABLED") {
-        return "PENDING_DISABLED";
-      }
-      if (currentStatus === "PENDING_DISABLED") {
-        return "ENABLED";
-      }
-      if (currentStatus === "PENDING_ENABLED") {
-        return "";
-      }
-      if (currentStatus === "") {
-        return "PENDING_ENABLED";
-      }
-    };
-
-    const handleEnable = (): void => {
-      setSoc2Data((prev) => ({
-        ...prev,
-        soc2_checks: {
-          ...prev.soc2_checks,
-          [checkKey]: {
-            ...prev.soc2_checks[checkKey],
-            enabled: !prev.soc2_checks[checkKey].enabled,
-            status: determineStatus(prev.soc2_checks[checkKey].status),
-          },
-        },
-      }));
-    };
-
-    return (
-      <CheckItemContainer hasMessage={hasMessage}>
-        {" "}
-        {/* Pass isExpanded as a prop */}
-        <CheckItemTop onClick={handleToggle}>
-          {status === "LOADING" && (
-            <Loading offset="0px" width="20px" height="20px" />
-          )}
-          {status === "PENDING_ENABLED" && <StatusIcon src={pending} />}
-          {status === "ENABLED" && <StatusIcon src={healthy} />}
-          {(status === "" || status === "PENDING_DISABLED") && (
-            <StatusIcon height="10px" src={failure} />
-          )}
-          <Spacer inline x={1} />
-          <Text style={{ marginLeft: "10px", flex: 1 }}>{checkLabel}</Text>
-          {enabled && (
-            <ExpandIcon className="material-icons" isExpanded={isExpanded}>
-              arrow_drop_down
-            </ExpandIcon>
-          )}
-        </CheckItemTop>
-        {isExpanded && hasMessage && (
-          <div style={{ marginLeft: "10px" }}>
-            <Spacer y={0.5} />
-            <Text>{checkData?.message}</Text>
-            <Spacer y={0.5} />
-            {checkData?.metadata &&
-              Object.entries(checkData.metadata).map(([key, value]) => (
-                <>
-                  <div key={key}>
-                    <ErrorMessageLabel>{key}:</ErrorMessageLabel>
-                    <ErrorMessageContent>{value}</ErrorMessageContent>
-                  </div>
-                </>
-              ))}
-            <Spacer y={0.5} />
-
-            {!checkData?.hideToggle && (
-              <>
-                <Container row spaced style={{ marginRight: "10px" }}>
-                  <ToggleRow
-                    isToggled={enabled || enableAll}
-                    onToggle={() => {
-                      handleEnable();
-                    }}
-                    disabled={
-                      readOnly ||
-                      enableAll ||
-                      (enabled &&
-                        checkData?.locked &&
-                        status !== "PENDING_ENABLED")
-                    }
-                    disabledTooltip={
-                      readOnly
-                        ? "Wait for provisioning to complete before editing this field."
-                        : enableAll
-                          ? "Global SOC 2 setting must be disabled to toggle this"
-                          : checkData?.disabledTooltip
-                    }
-                  >
-                    <Container row>
-                      <Text>{checkData.enabledField}</Text>
-                      <Spacer inline x={1} />
-                      <Text color="helper">{checkData.info}</Text>
-                    </Container>
-                  </ToggleRow>
-
-                  {checkData.link && (
-                    <Link
-                      onClick={() => {
-                        window.open(checkData.link, "_blank");
-                      }}
-                    >
-                      <TagIcon src={external_link} />
-                      More Info
-                    </Link>
-                  )}
-                </Container>
-                <Spacer y={0.5} />
-              </>
-            )}
-          </div>
-        )}
-      </CheckItemContainer>
-    );
-  };
-  return (
-    <>
-      <>
-        {/* <Fieldset>
-        <DonutChart soc2Data={soc2Data} />
-      </Fieldset> */}
-        <Spacer y={1} />
-        <AppearingDiv>
-          {Array.from(combinedKeys).map((checkKey) => (
-            <Soc2Item
-              key={checkKey}
-              checkKey={checkKey}
-              checkLabel={checkKey}
-            />
-          ))}
-        </AppearingDiv>
-      </>
-    </>
-  );
-};
-
-export default withRouter(SOC2Checks);
-
-const AppearingDiv = styled.div<{ color?: string }>`
-  animation: floatIn 0.5s;
-  animation-fill-mode: forwards;
-  display: flex;
-  flex-direction: column;
-  color: ${(props) => props.color || "#ffffff44"};
-
-  @keyframes floatIn {
-    from {
-      opacity: 0;
-      transform: translateY(20px);
-    }
-    to {
-      opacity: 1;
-      transform: translateY(0px);
-    }
-  }
-`;
-const StatusIcon = styled.img`
-  height: ${(props) => (props.height ? props.height : "14px")};
-`;
-
-const CheckItemContainer = styled.div`
-  display: flex;
-  flex-direction: column;
-  border: ${(props) =>
-    props.isExpanded
-      ? "2px solid #3a48ca"
-      : "1px solid " +
-      props.theme.border}; // Thicker and blue border if expanded
-  border-radius: 5px;
-  font-size: 13px;
-  width: 100%;
-  margin-bottom: 10px;
-  padding-left: 10px;
-  cursor: ${(props) => (props.hasMessage ? "pointer" : "default")};
-  background: ${(props) => props.theme.clickable.bg};
-`;
-
-const CheckItemTop = styled.div`
-  display: flex;
-  align-items: center;
-  padding: 10px;
-  background: ${(props) => props.theme.clickable.bg};
-`;
-
-const ExpandIcon = styled.i<{ isExpanded: boolean }>`
-  margin-left: 8px;
-  color: #ffffff66;
-  font-size: 20px;
-  cursor: pointer;
-  border-radius: 20px;
-  transform: ${(props) => (props.isExpanded ? "" : "rotate(-90deg)")};
-`;
-const ErrorMessageLabel = styled.span`
-  font-weight: bold;
-  margin-left: 10px;
-`;
-const ErrorMessageContent = styled.div`
-  font-family: "Courier New", Courier, monospace;
-  padding: 5px 10px;
-  border-radius: 4px;
-  margin-left: 10px;
-  user-select: text;
-  cursor: text;
-`;
-const TagIcon = styled.img`
-  height: 12px;
-  margin-right: 3px;
-`;

+ 0 - 118
dashboard/src/components/expanded-object/Header.tsx

@@ -1,118 +0,0 @@
-import DynamicLink from "components/DynamicLink";
-import React from "react";
-import styled from "styled-components";
-import TitleSection from "components/TitleSection";
-
-import leftArrow from "assets/left-arrow.svg";
-
-type Props = {
-  last_updated: string;
-  back_link: string;
-  name: string;
-  icon: string;
-  inline_title_items?: React.ReactNodeArray;
-  sub_title_items?: React.ReactNodeArray;
-  materialIconClass?: string;
-};
-
-const Header: React.FunctionComponent<Props> = (props) => {
-  const {
-    last_updated,
-    back_link,
-    icon,
-    name,
-    inline_title_items,
-    sub_title_items,
-    materialIconClass,
-  } = props;
-
-  return (
-    <>
-      <BreadcrumbRow>
-        <Breadcrumb to={back_link}>
-          <ArrowIcon src={leftArrow} />
-          <Wrap>Back</Wrap>
-        </Breadcrumb>
-      </BreadcrumbRow>
-      <HeaderWrapper>
-        <Title
-          icon={icon}
-          iconWidth="25px"
-          materialIconClass={materialIconClass}
-        >
-          {name}
-          <Flex>{inline_title_items}</Flex>
-        </Title>
-
-        {sub_title_items || (
-          <InfoWrapper>
-            <InfoText>Last updated {last_updated}</InfoText>
-          </InfoWrapper>
-        )}
-      </HeaderWrapper>
-    </>
-  );
-};
-
-export default Header;
-
-const Wrap = styled.div`
-  z-index: 999;
-`;
-
-const ArrowIcon = styled.img`
-  width: 15px;
-  margin-right: 8px;
-  opacity: 50%;
-`;
-
-const BreadcrumbRow = styled.div`
-  width: 100%;
-  display: flex;
-  justify-content: flex-start;
-`;
-
-const Breadcrumb = styled(DynamicLink)`
-  color: #aaaabb88;
-  font-size: 13px;
-  margin-bottom: 15px;
-  display: flex;
-  align-items: center;
-  margin-top: -10px;
-  z-index: 999;
-  padding: 5px;
-  padding-right: 7px;
-  border-radius: 5px;
-  cursor: pointer;
-  :hover {
-    background: #ffffff11;
-  }
-`;
-
-const HeaderWrapper = styled.div`
-  position: relative;
-  margin-bottom: 10px;
-`;
-
-const InfoWrapper = styled.div`
-  display: flex;
-  align-items: center;
-  width: auto;
-  justify-content: space-between;
-`;
-
-const InfoText = styled.span`
-  font-size: 13px;
-  color: #aaaabb66;
-`;
-
-const Title = styled(TitleSection)`
-  font-size: 16px;
-  margin-top: 4px;
-`;
-
-const Flex = styled.div`
-  display: flex;
-  align-items: center;
-  margin: 10px 0;
-`;

+ 0 - 34
dashboard/src/components/porter/TemplateComponent.tsx

@@ -1,34 +0,0 @@
-import React, { useEffect, useState } from "react";
-import styled from "styled-components";
-
-type Props = {
-};
-
-const TemplateComponent: React.FC<Props> = ({
-}) => {
-  const [isExpanded, setIsExpanded] = useState(false);
-
-  useEffect(() => {
-    // Do something
-  }, []);
-
-  return (
-    <StyledTemplateComponent>
-    </StyledTemplateComponent>
-  );
-};
-
-export default TemplateComponent;
-
-const StyledTemplateComponent = styled.div`
-width: 100%;
-animation: fadeIn 0.3s 0s;
-@keyframes fadeIn {
-  from {
-    opacity: 0;
-  }
-  to {
-    opacity: 1;
-  }
-}
-`;

+ 0 - 103
dashboard/src/components/repo-selector/NewGHAction.tsx

@@ -1,103 +0,0 @@
-import React, { Component } from "react";
-import styled from "styled-components";
-
-import { ChartType } from "shared/types";
-import { Context } from "shared/Context";
-import InputRow from "components/form-components/InputRow";
-
-import Loading from "../Loading";
-
-type PropsType = {
-  repoName: string;
-  dockerPath: string;
-  grid: number;
-  chart: ChartType;
-  imgURL: string;
-  setURL: (x: string) => void;
-};
-
-type StateType = {
-  trueDockerPath: string;
-  loading: boolean;
-  error: boolean;
-};
-
-export default class NewGHAction extends Component<PropsType, StateType> {
-  state = {
-    dockerRepo: "",
-    trueDockerPath: this.props.dockerPath,
-    loading: false,
-    error: false,
-  };
-
-  componentDidMount() {
-    if (this.props.dockerPath[0] === "/") {
-      this.setState({
-        trueDockerPath: this.props.dockerPath.substring(
-          1,
-          this.props.dockerPath.length
-        ),
-      });
-    }
-  }
-
-  renderConfirmation = () => {
-    let { loading } = this.state;
-    if (loading) {
-      return (
-        <LoadingWrapper>
-          <Loading />
-        </LoadingWrapper>
-      );
-    }
-
-    return (
-      <Holder>
-        <InputRow
-          disabled={true}
-          label="Git Repository"
-          type="text"
-          width="100%"
-          value={this.props.repoName}
-          setValue={(x: string) => console.log(x)}
-        />
-        <InputRow
-          disabled={true}
-          label="Dockerfile Path"
-          type="text"
-          width="100%"
-          value={this.state.trueDockerPath}
-          setValue={(x: string) => console.log(x)}
-        />
-        <InputRow
-          label="Docker Image Repository"
-          placeholder="Image Repo URL (ex. gcr.io/porter/mr-p)"
-          type="text"
-          width="100%"
-          value={this.props.imgURL}
-          setValue={(x: string) => this.props.setURL(x)}
-        />
-      </Holder>
-    );
-  };
-
-  render() {
-    return <div>{this.renderConfirmation()}</div>;
-  }
-}
-
-NewGHAction.contextType = Context;
-
-const Holder = styled.div`
-  padding: 0px 12px;
-`;
-
-const LoadingWrapper = styled.div`
-  padding: 30px 0px;
-  background: #ffffff11;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  font-size: 13px;
-  color: #ffffff44;
-`;

+ 0 - 449
dashboard/src/lib/hooks/useClusterResourceLimits.ts

@@ -1,449 +0,0 @@
-import { useEffect, useState } from "react";
-import {
-  Contract,
-  GKENodePoolType,
-  LoadBalancerType,
-  NodeGroupType,
-  NodePoolType,
-} from "@porter-dev/api-contracts";
-import { useQuery } from "@tanstack/react-query";
-import convert from "convert";
-import { match } from "ts-pattern";
-import { z } from "zod";
-
-import { azureMachineTypeDetails } from "components/azureUtils";
-import { AWS_INSTANCE_LIMITS } from "main/home/app-dashboard/validate-apply/services-settings/tabs/utils";
-
-import api from "shared/api";
-
-const DEFAULT_INSTANCE_CLASS = "t3";
-const DEFAULT_INSTANCE_SIZE = "medium";
-
-export type ClientLoadBalancerType = "ALB" | "NLB" | "UNSPECIFIED";
-
-const encodedContractValidator = z.object({
-  ID: z.number(),
-  CreatedAt: z.string(),
-  UpdatedAt: z.string(),
-  DeletedAt: z.string().nullable(),
-  id: z.string(),
-  base64_contract: z.string(),
-  cluster_id: z.number(),
-  project_id: z.number(),
-  condition: z.string(),
-  condition_metadata: z.record(z.any()).nullable(),
-});
-
-export type NodeGroup = {
-  instanceType: string;
-  minInstances: number;
-  maxInstances: number;
-  nodeGroupType: string;
-  isStateful?: boolean;
-};
-
-export type EksKind = {
-  clusterName: string;
-  clusterVersion: string;
-  cidrRange: string;
-  region: string;
-  nodeGroups: NodeGroup[];
-  loadBalancer: {
-    loadBalancerType: string;
-  };
-  logging: Record<string, unknown>;
-  network: {
-    vpcCidr: string;
-    serviceCidr: string;
-  };
-};
-
-export type GKEKind = {
-  clusterName: string;
-  clusterVersion: string;
-  region: string;
-  nodePools: NodePools[];
-  user: {
-    id: number;
-  };
-  network: {
-    cidrRange: string;
-    controlPlaneCidr: string;
-    podCidr: string;
-    serviceCidr: string;
-  };
-};
-
-export type NodePools = {
-  instanceType: string;
-  minInstances: number;
-  maxInstances: number;
-  nodePoolType: string;
-  isStateful?: boolean;
-  additionalTaints?: string[];
-};
-
-const clusterNodesValidator = z
-  .object({
-    labels: z
-      .object({
-        "beta.kubernetes.io/instance-type": z.string().nullish(),
-        "porter.run/workload-kind": z.string().nullish(),
-      })
-      .optional(),
-  })
-  .transform((data) => {
-    const defaultResources = {
-      maxCPU:
-        AWS_INSTANCE_LIMITS[DEFAULT_INSTANCE_CLASS][DEFAULT_INSTANCE_SIZE].vCPU,
-      maxRAM:
-        AWS_INSTANCE_LIMITS[DEFAULT_INSTANCE_CLASS][DEFAULT_INSTANCE_SIZE].RAM,
-      maxGPU: 1,
-    };
-    if (!data.labels) {
-      return defaultResources;
-    }
-    const workloadKind = data.labels["porter.run/workload-kind"];
-    if (
-      !workloadKind ||
-      (workloadKind !== "application" && workloadKind !== "custom")
-    ) {
-      return defaultResources;
-    }
-    const instanceType = data.labels["beta.kubernetes.io/instance-type"];
-
-    if (!instanceType) {
-      return defaultResources;
-    }
-
-    // Azure instance types are all prefixed with "Standard_"
-    if (instanceType.startsWith("Standard_")) {
-      const azureMachineType = azureMachineTypeDetails(instanceType);
-      if (azureMachineType) {
-        const { vCPU, RAM, GPU } = azureMachineType.resources;
-        return {
-          maxCPU: vCPU,
-          maxRAM: RAM,
-          maxGPU: GPU || 1,
-        };
-      } else {
-        return defaultResources;
-      }
-    }
-
-    let parsedType;
-    if (instanceType && instanceType.includes(".")) {
-      parsedType = z
-        .tuple([z.string(), z.string()])
-        .safeParse(instanceType.split("."));
-    } else if (instanceType && instanceType.includes("-")) {
-      const [instanceClass, ...instanceSizeParts] = instanceType.split("-");
-      const instanceSize = instanceSizeParts.join("-");
-      parsedType = z
-        .tuple([z.string(), z.string()])
-        .safeParse([instanceClass, instanceSize]);
-    } else {
-      return defaultResources; // Return defaults if instanceType format is not recognized
-    }
-
-    if (!parsedType.success) {
-      return defaultResources;
-    }
-
-    const [instanceClass, instanceSize] = parsedType.data;
-    if (AWS_INSTANCE_LIMITS[instanceClass]?.[instanceSize]) {
-      const { vCPU, RAM, GPU } =
-        AWS_INSTANCE_LIMITS[instanceClass][instanceSize];
-      return {
-        maxCPU: vCPU,
-        maxRAM: RAM,
-        maxGPU: GPU || 1,
-      };
-    }
-    return defaultResources;
-  });
-
-export const useClusterResourceLimits = ({
-  projectId,
-  clusterId,
-  clusterStatus,
-}: {
-  projectId: number | undefined;
-  clusterId: number | undefined;
-  clusterStatus: string | undefined;
-}): {
-  maxCPU: number;
-  maxRAM: number;
-  // defaults indicate the resources assigned to new services
-  defaultCPU: number;
-  defaultRAM: number;
-  clusterContainsGPUNodes: boolean;
-  maxGPU: number;
-  clusterIngressIp: string;
-  loadBalancerType: ClientLoadBalancerType;
-} => {
-  const SMALL_INSTANCE_UPPER_BOUND = 0.75;
-  const LARGE_INSTANCE_UPPER_BOUND = 0.9;
-  const DEFAULT_MULTIPLIER = 0.125;
-  const [clusterContainsGPUNodes, setClusterContainsGPUNodes] = useState(false);
-  const [maxGPU, setMaxGPU] = useState(1);
-  const [maxCPU, setMaxCPU] = useState(
-    AWS_INSTANCE_LIMITS[DEFAULT_INSTANCE_CLASS][DEFAULT_INSTANCE_SIZE].vCPU *
-      SMALL_INSTANCE_UPPER_BOUND
-  );
-  const [maxRAM, setMaxRAM] = useState(
-    // round to nearest 100
-    Math.round(
-      (convert(
-        AWS_INSTANCE_LIMITS[DEFAULT_INSTANCE_CLASS][DEFAULT_INSTANCE_SIZE].RAM,
-        "GiB"
-      ).to("MB") *
-        SMALL_INSTANCE_UPPER_BOUND) /
-        100
-    ) * 100
-  );
-  const [defaultCPU, setDefaultCPU] = useState(
-    AWS_INSTANCE_LIMITS[DEFAULT_INSTANCE_CLASS][DEFAULT_INSTANCE_SIZE].vCPU *
-      DEFAULT_MULTIPLIER
-  );
-  const [defaultRAM, setDefaultRAM] = useState(
-    // round to nearest 100
-    Math.round(
-      (convert(
-        AWS_INSTANCE_LIMITS[DEFAULT_INSTANCE_CLASS][DEFAULT_INSTANCE_SIZE].RAM,
-        "GiB"
-      ).to("MB") *
-        DEFAULT_MULTIPLIER) /
-        100
-    ) * 100
-  );
-  const [clusterIngressIp, setClusterIngressIp] = useState<string>("");
-  const [loadBalancerType, setLoadBalancerType] =
-    useState<ClientLoadBalancerType>("UNSPECIFIED");
-
-  const getClusterNodes = useQuery(
-    ["getClusterNodes", projectId, clusterId],
-    async () => {
-      if (!projectId || !clusterId || clusterId === -1) {
-        return await Promise.resolve([]);
-      }
-
-      const res = await api.getClusterNodes(
-        "<token>",
-        {},
-        {
-          project_id: projectId,
-          cluster_id: clusterId,
-        }
-      );
-      return await z.array(clusterNodesValidator).parseAsync(res.data);
-    },
-    {
-      enabled: !!projectId && !!clusterId,
-      refetchOnWindowFocus: false,
-      retry: false,
-    }
-  );
-
-  const { data: contract } = useQuery(
-    ["getContracts", projectId, clusterId, clusterStatus],
-    async () => {
-      if (!projectId || !clusterId || clusterId === -1) {
-        return;
-      }
-
-      const res = await api.getContracts(
-        "<token>",
-        {},
-        { project_id: projectId }
-      );
-      const contracts = await z
-        .array(encodedContractValidator)
-        .parseAsync(res.data);
-      if (contracts.length) {
-        const latestContract = contracts
-          .filter((contract) => contract.cluster_id === clusterId)
-          .sort(
-            (a, b) =>
-              new Date(b.CreatedAt).getTime() - new Date(a.CreatedAt).getTime()
-          )[0];
-        const decodedContract = Contract.fromJsonString(
-          atob(latestContract.base64_contract)
-        );
-        return decodedContract.cluster;
-      }
-    },
-    {
-      enabled: !!projectId,
-      refetchOnWindowFocus: false,
-      retry: false,
-    }
-  );
-
-  useEffect(() => {
-    if (getClusterNodes.isSuccess) {
-      const data = getClusterNodes.data;
-      if (data.length) {
-        // this logic handles CPU and RAM independently - we might want to change this later
-        const maxCPU = data.reduce((acc, curr) => {
-          return Math.max(acc, curr.maxCPU);
-        }, 0);
-        const maxRAM = data.reduce((acc, curr) => {
-          return Math.max(acc, curr.maxRAM);
-        }, 0);
-        const maxGPU = data.reduce((acc, curr) => {
-          return Math.max(acc, curr.maxGPU);
-        }, 0);
-        let maxMultiplier = SMALL_INSTANCE_UPPER_BOUND;
-        // if the instance type has more than 16 GB ram, we use 90% of the ram/cpu
-        // otherwise, we use 75%
-        if (maxRAM > 16) {
-          maxMultiplier = LARGE_INSTANCE_UPPER_BOUND;
-        }
-        // round down to nearest 0.5 cores
-        const newMaxCPU = Math.floor(maxCPU * maxMultiplier * 2) / 2;
-        // round down to nearest 100 MB
-        const newMaxRAM =
-          Math.round((convert(maxRAM, "GiB").to("MB") * maxMultiplier) / 100) *
-          100;
-        setMaxCPU(newMaxCPU);
-        setMaxRAM(newMaxRAM);
-        setMaxGPU(maxGPU);
-        setDefaultCPU(Number((newMaxCPU * DEFAULT_MULTIPLIER).toFixed(2)));
-        setDefaultRAM(Number((newMaxRAM * DEFAULT_MULTIPLIER).toFixed(0)));
-      }
-    }
-  }, [getClusterNodes]);
-
-  const getCluster = useQuery(
-    ["getClusterIngressIp", projectId, clusterId],
-    async () => {
-      if (!projectId || !clusterId || clusterId === -1) {
-        return await Promise.resolve({ ingress_ip: "" });
-      }
-
-      const res = await api.getCluster(
-        "<token>",
-        {},
-        {
-          project_id: projectId,
-          cluster_id: clusterId,
-        }
-      );
-
-      return await z.object({ ingress_ip: z.string() }).parseAsync(res.data);
-    },
-    {
-      enabled: !!projectId && !!clusterId,
-      refetchOnWindowFocus: false,
-      retry: false,
-    }
-  );
-
-  useEffect(() => {
-    if (getCluster.isSuccess) {
-      setClusterIngressIp(getCluster.data.ingress_ip);
-    }
-  }, [getCluster]);
-
-  useEffect(() => {
-    if (contract) {
-      const containsCustomNodeGroup = match(contract)
-        .with({ kindValues: { case: "eksKind" } }, (c) => {
-          return c.kindValues.value.nodeGroups.some(
-            (ng) =>
-              (ng.nodeGroupType === NodeGroupType.CUSTOM &&
-                (ng.instanceType.includes("g4dn") ||
-                  ng.instanceType.includes("p4d"))) ||
-              (ng.nodeGroupType === NodeGroupType.APPLICATION &&
-                (ng.instanceType.includes("g4dn") ||
-                  ng.instanceType.includes("p4d")))
-          );
-        })
-        .with({ kindValues: { case: "gkeKind" } }, (c) => {
-          return c.kindValues.value.nodePools.some(
-            (ng) =>
-              (ng.nodePoolType === GKENodePoolType.GKE_NODE_POOL_TYPE_CUSTOM &&
-                ng.instanceType.includes("n1")) ||
-              (ng.nodePoolType ===
-                GKENodePoolType.GKE_NODE_POOL_TYPE_APPLICATION &&
-                ng.instanceType.includes("n1"))
-          );
-        })
-        .with({ kindValues: { case: "aksKind" } }, (c) => {
-          return c.kindValues.value.nodePools.some(
-            (ng) => ng.nodePoolType === NodePoolType.CUSTOM
-          );
-        })
-        .otherwise(() => false);
-
-      const loadBalancerType: ClientLoadBalancerType = match(contract)
-        .with({ kindValues: { case: "eksKind" } }, (c) => {
-          const loadBalancer = c.kindValues.value.loadBalancer;
-          if (!loadBalancer) {
-            return "UNSPECIFIED";
-          }
-          return match(loadBalancer.loadBalancerType)
-            .with(LoadBalancerType.ALB, (): ClientLoadBalancerType => "ALB")
-            .with(LoadBalancerType.NLB, (): ClientLoadBalancerType => "NLB")
-            .otherwise((): ClientLoadBalancerType => "UNSPECIFIED");
-        })
-        .otherwise(() => "UNSPECIFIED");
-
-      // console.log(gpu);
-      // setMaxGPU(gpu);
-      setClusterContainsGPUNodes(containsCustomNodeGroup);
-      setLoadBalancerType(loadBalancerType);
-    }
-  }, [contract]);
-
-  return {
-    maxCPU,
-    maxRAM,
-    defaultCPU,
-    defaultRAM,
-    clusterContainsGPUNodes,
-    maxGPU,
-    clusterIngressIp,
-    loadBalancerType,
-  };
-};
-
-// this function returns the fraction which the resource sliders 'snap' to when the user turns on smart optimization
-export const lowestClosestResourceMultipler = (
-  min: number,
-  max: number,
-  value: number
-): number => {
-  const fractions = [0.5, 0.25, 0.125];
-
-  for (const fraction of fractions) {
-    const newValue = fraction * (max - min) + min;
-    if (newValue <= value) {
-      return fraction;
-    }
-  }
-
-  return 0.125; // Return 0 if no fraction rounds down
-};
-
-// this function is used to snap both resource sliders in unison when one is changed
-export const closestMultiplier = (
-  min: number,
-  max: number,
-  value: number
-): number => {
-  const fractions = [0.5, 0.25, 0.125];
-  let closestFraction = 0.125;
-  for (const fraction of fractions) {
-    const newValue = fraction * (max - min) + min;
-    if (
-      Math.abs(newValue - value) <
-      Math.abs(closestFraction * (max - min) + min - value)
-    ) {
-      closestFraction = fraction;
-    }
-  }
-
-  return closestFraction;
-};

+ 0 - 35
dashboard/src/lib/hooks/useResizeObserver.ts

@@ -1,35 +0,0 @@
-import { useLayoutEffect, useRef } from "react";
-
-/*
- *
- * useResizeObserver takes in a callback function and returns a ref
- * that can be attached to a DOM element. The callback function will
- * be called whenever the DOM element is resized.
- *
- */
-function useResizeObserver<T extends HTMLElement>(
-  callback: (target: T) => void
-) {
-  const ref = useRef<T>(null);
-
-  useLayoutEffect(() => {
-    const element = ref?.current;
-
-    if (!element) {
-      return;
-    }
-
-    const observer = new ResizeObserver(() => {
-      callback(element);
-    });
-
-    observer.observe(element);
-    return () => {
-      observer.disconnect();
-    };
-  }, [callback, ref]);
-
-  return ref;
-}
-
-export default useResizeObserver;

+ 0 - 301
dashboard/src/main/home/WelcomeForm.tsx

@@ -1,301 +0,0 @@
-import React, { useContext, useState } from "react";
-import styled from "styled-components";
-import { CSSTransition } from "react-transition-group";
-import api from "shared/api";
-
-import { Context } from "shared/Context";
-
-type Props = {
-  closeForm: () => void;
-};
-
-type StateType = {
-  active: boolean;
-};
-
-const WelcomeForm: React.FunctionComponent<Props> = ({}) => {
-  const context = useContext(Context);
-  const [active, setActive] = useState(true);
-  const [isCompany, setIsCompany] = useState(true);
-  const [name, setName] = useState("");
-  const [company, setCompany] = useState("");
-  const [role, setRole] = useState("unspecified");
-
-  const submitForm = () => {
-    api
-      .postWelcome(
-        "<token>",
-        {
-          email: context.user && context.user.email,
-          name,
-          isCompany,
-          company,
-          role,
-        },
-        {}
-      )
-      .then(() => {
-        localStorage.setItem("welcomed", "true");
-        setActive(false);
-      })
-      .catch((err) => console.log(err));
-  };
-
-  const renderContents = () => {
-    return (
-      <FadeWrapper>
-        <Title>Welcome to Porter</Title>
-        <Subtitle>Just a few things before getting started.</Subtitle>
-        <SubtitleAlt>
-          <Num>1</Num> What is your name? *
-        </SubtitleAlt>
-        <Input
-          placeholder="John Doe"
-          value={name}
-          onChange={(e: any) => setName(e.target.value)}
-        />
-        <SubtitleAlt>
-          <Num>2</Num> What is your company website? *
-        </SubtitleAlt>
-        <Input
-          placeholder="ex: https://porter.run"
-          value={company}
-          onChange={(e: any) => setCompany(e.target.value)}
-        />
-        <SubtitleAlt>
-          <Num>3</Num> What is your role? *
-        </SubtitleAlt>
-        <RadioButton
-          onClick={() => setRole("founder")}
-          selected={role === "founder"}
-        >
-          <i className="material-icons-round">
-            {role === "founder" ? "check_box" : "check_box_outline_blank"}
-          </i>{" "}
-          Founder
-        </RadioButton>
-        <RadioButton
-          onClick={() => setRole("developer")}
-          selected={role === "developer"}
-        >
-          <i className="material-icons-round">
-            {role === "developer" ? "check_box" : "check_box_outline_blank"}
-          </i>{" "}
-          Developer
-        </RadioButton>
-        <RadioButton
-          onClick={() => setRole("devops")}
-          selected={role === "devops"}
-        >
-          <i className="material-icons-round">
-            {role === "devops" ? "check_box" : "check_box_outline_blank"}
-          </i>{" "}
-          DevOps
-        </RadioButton>
-
-        <Submit
-          isDisabled={!company || role === "unspecified"}
-          onClick={() => company && role !== "unspecified" && submitForm()}
-        >
-          <i className="material-icons-round">check</i> Done
-        </Submit>
-      </FadeWrapper>
-    );
-  };
-
-  return (
-    <CSSTransition
-      in={active}
-      timeout={500}
-      classNames="alert"
-      unmountOnExit
-      onEnter={() => setActive(true)}
-      onExited={() => setActive(false)}
-    >
-      <StyledWelcomeForm>
-        <div>
-          {renderContents()}
-          <br />
-          <br />
-        </div>
-      </StyledWelcomeForm>
-    </CSSTransition>
-  );
-};
-
-export default WelcomeForm;
-
-const Circle = styled.div`
-  width: 13px;
-  height: 13px;
-  border-radius: 20px;
-  background: #ffffff11;
-  margin-right: 12px;
-  border: 1px solid #aaaabb;
-`;
-
-const FadeWrapper = styled.div`
-  background: #202227;
-  opacity: 0;
-  animation: fadeIn 0.7s 0s;
-  animation-fill-mode: forwards;
-`;
-
-const Num = styled.div`
-  display: flex;
-  align-items: center;
-  margin-right: 15px;
-  justify-content: center;
-  width: 30px;
-  height: 30px;
-  border: 1px solid #ffffff;
-`;
-
-const Option = styled.div`
-  width: 500px;
-  max-width: 80vw;
-  height: 50px;
-  background: #ffffff22;
-  display: flex;
-  align-items: center;
-  margin-top: 15px;
-  border: 1px solid #aaaabb;
-  border-radius: 5px;
-  padding-left: 15px;
-  cursor: pointer;
-  :hover {
-    background: #ffffff44;
-  }
-
-  > i {
-    font-size: 20px;
-    margin-right: 12px;
-    color: #aaaabb;
-  }
-
-  opacity: 0;
-  animation: slideIn 0.7s 1.3s;
-  animation-fill-mode: forwards;
-
-  @keyframes slideIn {
-    from {
-      opacity: 0;
-      transform: translateX(-30px);
-    }
-    to {
-      opacity: 1;
-      transform: translateX(0);
-    }
-  }
-`;
-
-const Submit = styled(Option)<{ isDisabled: boolean }>`
-  border: 0;
-  opacity: 0;
-  animation: fadeIn 0.7s 0.5s;
-  animation-fill-mode: forwards;
-  margin-top: 35px;
-  cursor: ${(props) => (props.isDisabled ? "not-allowed" : "pointer")};
-  background: ${(props) => (props.isDisabled ? "#aaaabb" : "#616FEEcc")};
-  :hover {
-    filter: ${(props) => (props.isDisabled ? "" : "brightness(130%)")};
-    background: ${(props) => (props.isDisabled ? "#aaaabb" : "#616FEEcc")};
-  }
-
-  > i {
-    color: #ffffff;
-  }
-`;
-
-const RadioButton = styled(Option)<{ selected: boolean }>`
-  opacity: 0;
-  background: ${(props) => (props.selected ? "#ffffff44" : "#ffffff22")};
-  animation: fadeIn 0.5s 0.2s;
-  animation-fill-mode: forwards;
-
-  > div {
-    background: ${(props) => (props.selected ? "#ffffff44" : "")};
-  }
-`;
-
-const Input = styled.input`
-  width: 500px;
-  max-width: 80vw;
-  height: 50px;
-  background: #ffffff22;
-  font-size: 18px;
-  display: flex;
-  align-items: center;
-  margin-top: 0px;
-  color: #ffffff;
-  border: 1px solid #aaaabb;
-  border-radius: 5px;
-  padding-left: 15px;
-  margin-bottom: 40px;
-
-  opacity: 0;
-  animation: fadeIn 0.5s 0.2s;
-  animation-fill-mode: forwards;
-`;
-
-const Subtitle = styled.div<{ delay?: string }>`
-  margin: 20px 0 30px;
-  color: #aaaabb;
-
-  opacity: 0;
-  animation: fadeIn 0.5s ${(props) => props.delay || "0.2s"};
-  animation-fill-mode: forwards;
-`;
-
-const SubtitleAlt = styled(Subtitle)`
-  margin: -5px 0 30px;
-  color: white;
-  display: flex;
-  align-items: center;
-  animation: fadeIn 0.5s 0.2s;
-  animation-fill-mode: forwards;
-`;
-
-const Title = styled.div`
-  color: white;
-
-  font-size: 26px;
-  margin-bottom: 5px;
-  display: flex;
-  align-items: center;
-
-  opacity: 0;
-  animation: fadeIn 0.5s 0.2s;
-  animation-fill-mode: forwards;
-
-  @keyframes fadeIn {
-    from {
-      opacity: 0;
-    }
-    to {
-      opacity: 1;
-    }
-  }
-`;
-
-const StyledWelcomeForm = styled.div`
-  position: fixed;
-  top: 0;
-  left: 0;
-  width: 100%;
-  height: 100%;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  z-index: 1;
-  background: #202227;
-
-  &.alert-exit {
-    opacity: 1;
-  }
-  &.alert-exit-active {
-    opacity: 0;
-    transform: translateY(-100px);
-    transition: opacity 500ms, transform 1000ms;
-  }
-`;

+ 0 - 108
dashboard/src/main/home/app-dashboard/app-view/tabs/preview-environments/PreviewEnvironmentSettings.tsx

@@ -1,108 +0,0 @@
-import Button from "components/porter/Button";
-import Spacer from "components/porter/Spacer";
-import Text from "components/porter/Text";
-import React from "react";
-import { Link } from "react-router-dom";
-import { useLatestRevision } from "../../LatestRevisionContext";
-import { useQuery } from "@tanstack/react-query";
-import api from "shared/api";
-import { useGithubWorkflow } from "lib/hooks/useGithubWorkflow";
-import styled from "styled-components";
-import healthy from "assets/status-healthy.png";
-import Icon from "components/porter/Icon";
-import { z } from "zod";
-import { PorterApp } from "@porter-dev/api-contracts";
-
-type Props = {};
-
-const PreviewEnvironmentSettings: React.FC<Props> = ({}) => {
-  const { porterApp, clusterId, projectId } = useLatestRevision();
-
-  const { data: templateExists, status } = useQuery(
-    ["getAppTemplate", projectId, clusterId, porterApp.name],
-    async () => {
-      try {
-        const res = await api.getAppTemplate(
-          "<token>",
-          {},
-          {
-            project_id: projectId,
-            cluster_id: clusterId,
-            porter_app_name: porterApp.name,
-          }
-        );
-
-        const data = await z
-          .object({
-            template_b64_app_proto: z.string(),
-            app_env: z.object({
-              variables: z.record(z.string()).default({}),
-              secret_variables: z.record(z.string()).default({}),
-            }),
-          })
-          .parseAsync(res.data);
-
-        const template = PorterApp.fromJsonString(
-          atob(data.template_b64_app_proto),
-          {
-            ignoreUnknownFields: true,
-          }
-        );
-
-        return {
-          template,
-          env: data.app_env,
-        };
-      } catch (err) {
-        return null;
-      }
-    }
-  );
-
-  const { githubWorkflowFilename, isLoading } = useGithubWorkflow({
-    porterApp,
-    fileNames: [`porter_preview_${porterApp.name}.yml`],
-  });
-
-  if (status === "loading" || isLoading) {
-    return null;
-  }
-
-  return (
-    <>
-      {templateExists && githubWorkflowFilename ? (
-        <EnabledContainer>
-          <Text size={16}>Preview Environments Enabled</Text>
-          <Icon src={healthy} />
-        </EnabledContainer>
-      ) : (
-        <Text size={16}>
-          Enable preview environments for "{porterApp.name}"
-        </Text>
-      )}
-      <Spacer y={0.5} />
-      <Text color="helper">
-        {templateExists && githubWorkflowFilename
-          ? "Preview environments are enabled for this app"
-          : "Setup your app to automatically create preview environments for each pull request."}
-      </Text>
-      <Spacer y={0.5} />
-      <Link to={`/preview-environments/configure?app_name=${porterApp.name}`}>
-        <Button type="button">
-          {templateExists && githubWorkflowFilename
-            ? "Update Settings"
-            : "Enable"}
-        </Button>
-      </Link>
-      <Spacer y={1} />
-    </>
-  );
-};
-
-export default PreviewEnvironmentSettings;
-
-const EnabledContainer = styled.div`
-  display: flex;
-  align-items: center;
-  column-gap: 0.75rem;
-`;

+ 0 - 137
dashboard/src/main/home/app-dashboard/build-settings/AdvancedBuildSettings.tsx

@@ -1,137 +0,0 @@
-import React, { useState } from "react";
-import styled from "styled-components";
-import Text from "components/porter/Text";
-import Spacer from "components/porter/Spacer";
-import Input from "components/porter/Input";
-import AnimateHeight from "react-animate-height";
-import Select from "components/porter/Select";
-import { BuildMethod, PorterApp } from "../types/porterApp";
-import BuildpackSettings from "./buildpacks/BuildpackSettings";
-import _ from "lodash";
-
-interface AdvancedBuildSettingsProps {
-  porterApp: PorterApp;
-  updatePorterApp: (attrs: Partial<PorterApp>) => void;
-  autoDetectBuildpacks: boolean;
-  buildView: BuildMethod;
-  setBuildView: (buildView: BuildMethod) => void;
-}
-
-const AdvancedBuildSettings: React.FC<AdvancedBuildSettingsProps> = ({
-  porterApp,
-  updatePorterApp,
-  autoDetectBuildpacks,
-  buildView,
-  setBuildView,
-}) => {
-  const [showSettings, setShowSettings] = useState<boolean>(false);
-
-  return (
-    <>
-      <StyledAdvancedBuildSettings
-        showSettings={showSettings}
-        isCurrent={true}
-        onClick={() => {
-          setShowSettings(!showSettings);
-        }}
-      >
-        {buildView == "docker" ? (
-          <AdvancedBuildTitle>
-            <i className="material-icons dropdown">arrow_drop_down</i>
-            Configure Dockerfile settings
-          </AdvancedBuildTitle>
-        ) : (
-          <AdvancedBuildTitle>
-            <i className="material-icons dropdown">arrow_drop_down</i>
-            Configure buildpack settings
-          </AdvancedBuildTitle>
-        )}
-      </StyledAdvancedBuildSettings>
-
-      <AnimateHeight height={showSettings ? "auto" : 0} duration={1000}>
-        <StyledSourceBox>
-          <Select
-            value={buildView}
-            width="300px"
-            options={[
-              { value: "docker", label: "Docker" },
-              { value: "buildpacks", label: "Buildpacks" },
-            ]}
-            setValue={(option: string) => setBuildView(option as BuildMethod)}
-            label="Build method"
-          />
-          {buildView === "docker"
-            ?
-            <>
-              <Spacer y={0.5} />
-              <Text color="helper">Dockerfile path (absolute path)</Text>
-              <Spacer y={0.5} />
-              <Input
-                placeholder="ex: ./Dockerfile"
-                value={porterApp.dockerfile}
-                width="300px"
-                setValue={(val: string) => updatePorterApp({ dockerfile: val })}
-              />
-              <Spacer y={0.5} />
-            </>
-            : <BuildpackSettings
-              porterApp={porterApp}
-              updatePorterApp={updatePorterApp}
-              autoDetectBuildpacks={autoDetectBuildpacks}
-            />}
-        </StyledSourceBox>
-      </AnimateHeight>
-    </>
-  );
-};
-
-export default AdvancedBuildSettings;
-
-const StyledAdvancedBuildSettings = styled.div`
-  color: ${({ showSettings }) => (showSettings ? "white" : "#aaaabb")};
-  background: ${({ theme }) => theme.fg};
-  border: 1px solid #494b4f;
-  :hover {
-    border: 1px solid #7a7b80;
-    color: white;
-  }
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  border-radius: 5px;
-  height: 40px;
-  font-size: 13px;
-  width: 100%;
-  padding-left: 10px;
-  cursor: pointer;
-  border-bottom-left-radius: ${({ showSettings }) => showSettings && "0px"};
-  border-bottom-right-radius: ${({ showSettings }) => showSettings && "0px"};
-
-  .dropdown {
-    margin-right: 8px;
-    font-size: 20px;
-    cursor: pointer;
-    border-radius: 20px;
-    transform: ${(props: { showSettings: boolean; isCurrent: boolean }) =>
-    props.showSettings ? "" : "rotate(-90deg)"};
-  }
-`;
-
-const AdvancedBuildTitle = styled.div`
-  display: flex;
-  align-items: center;
-`;
-
-const StyledSourceBox = styled.div`
-  width: 100%;
-  color: #ffffff;
-  padding: 25px 35px 25px;
-  position: relative;
-  font-size: 13px;
-  border-radius: 5px;
-  background: ${(props) => props.theme.fg};
-  border: 1px solid #494b4f;
-  border-top: 0px;
-  border-top-left-radius: 0px;
-  border-top-right-radius: 0px;
-`;

+ 0 - 195
dashboard/src/main/home/app-dashboard/build-settings/BuildSettingsTab.tsx

@@ -1,195 +0,0 @@
-import React, {
-  useContext,
-  useState,
-} from "react";
-import Text from "components/porter/Text";
-import Spacer from "components/porter/Spacer";
-import { CreateUpdatePorterAppOptions } from "shared/types";
-import { Context } from "shared/Context";
-
-import api from "shared/api";
-import { AxiosError } from "axios";
-import Button from "components/porter/Button";
-import Checkbox from "components/porter/Checkbox";
-import SharedBuildSettings from "./SharedBuildSettings";
-import { BuildMethod, PorterApp } from "../types/porterApp";
-import _ from "lodash";
-
-type Props = {
-  porterApp: PorterApp;
-  setTempPorterApp: (app: PorterApp) => void;
-  updatePorterApp: (options: Partial<CreateUpdatePorterAppOptions>) => Promise<void>;
-  clearStatus: () => void;
-  buildView: BuildMethod;
-  setBuildView: (buildView: BuildMethod) => void;
-};
-
-const BuildSettingsTab: React.FC<Props> = ({
-  porterApp,
-  setTempPorterApp,
-  clearStatus,
-  updatePorterApp,
-  buildView,
-  setBuildView,
-}) => {
-  const { setCurrentError, currentCluster, currentProject } = useContext(Context);
-  const [redeployOnSave, setRedeployOnSave] = useState(true);
-  const [runningWorkflowURL, setRunningWorkflowURL] = useState("");
-
-  const [buttonStatus, setButtonStatus] = useState<
-    "loading" | "success" | string
-  >("");
-
-  const triggerWorkflow = async () => {
-    try {
-      if (currentProject == null || currentCluster == null) {
-        return;
-      }
-
-      const res = await api.reRunGHWorkflow(
-        "",
-        {},
-        {
-          project_id: currentProject.id,
-          cluster_id: currentCluster.id,
-          git_installation_id: porterApp.git_repo_id,
-          owner: porterApp.repo_name?.split("/")[0],
-          name: porterApp.repo_name?.split("/")[1],
-          branch: porterApp.git_branch,
-          filename: "porter_stack_" + porterApp.name + ".yml",
-        }
-      );
-      if (res.data != null) {
-        window.open(res.data, "_blank", "noreferrer")
-      }
-    } catch (error) {
-      if (!error?.response) {
-        throw error;
-      }
-
-      let tmpError: AxiosError = error;
-
-      /**
-       * @smell
-       * Currently the expanded chart is clearing all the state when a chart update is triggered (saveEnvVariables).
-       * Temporary usage of setCurrentError until a context is applied to keep the state of the ReRunError during re renders.
-       */
-
-      if (tmpError.response.status === 400) {
-        // setReRunError({
-        //   title: "No previous run found",
-        //   description:
-        //     "There are no previous runs for this workflow, please trigger manually a run before changing the build settings.",
-        // });
-        setCurrentError(
-          "There are no previous runs for this workflow. Please manually trigger a run before changing build settings."
-        );
-        return;
-      }
-
-      if (tmpError.response.status === 409) {
-        // setReRunError({
-        //   title: "The workflow is still running",
-        //   description:
-        //     'If you want to make more changes, please choose the option "Save" until the workflow finishes.',
-        // });
-
-        if (typeof tmpError.response.data === "string") {
-          setRunningWorkflowURL(tmpError.response.data);
-        }
-        setCurrentError(
-          'The workflow is still running. You can "Save" the current build settings for the next workflow run and view the current status of the workflow here: ' +
-          tmpError.response.data
-        );
-        return;
-      }
-
-      if (tmpError.response.status === 404) {
-        let description = "No action file matching this deployment was found.";
-        if (typeof tmpError.response.data === "string") {
-          const filename = tmpError.response.data;
-          description = description.concat(
-            `Please check that the file "${filename}" exists in your repository.`
-          );
-        }
-        // setReRunError({
-        //   title: "The action doesn't seem to exist",
-        //   description,
-        // });
-
-        setCurrentError(description);
-        return;
-      }
-      throw error;
-    }
-  };
-
-  const saveConfig = async () => {
-    try {
-      await updatePorterApp({});
-    } catch (err) {
-      console.log(err);
-    }
-  };
-
-  const handleSave = async () => {
-    setButtonStatus("loading");
-
-    try {
-      await saveConfig();
-      setButtonStatus("success");
-    } catch (error) {
-      setButtonStatus("Something went wrong");
-      console.log(error);
-    }
-  };
-
-  const handleSaveAndReDeploy = async () => {
-    setButtonStatus("loading");
-
-    try {
-      await saveConfig();
-      await triggerWorkflow();
-      setButtonStatus("success");
-      clearStatus();
-    } catch (error) {
-      setButtonStatus("Something went wrong");
-      console.log(error);
-    }
-  };
-  return (
-    <>
-      <SharedBuildSettings
-        porterApp={porterApp}
-        updatePorterApp={(attrs: Partial<PorterApp>) => setTempPorterApp(PorterApp.setAttributes(porterApp, attrs))}
-        setPorterYaml={() => { }}
-        autoDetectionOn={false}
-        canChangeRepo={false}
-        buildView={buildView}
-        setBuildView={setBuildView}
-      />
-      <Spacer y={1} />
-      <Checkbox
-        checked={redeployOnSave}
-        toggleChecked={() => setRedeployOnSave(!redeployOnSave)}
-      >
-        <Text>Re-run build and deploy on save</Text>
-      </Checkbox>
-      <Spacer y={1} />
-      <Button
-        onClick={() => {
-          if (redeployOnSave) {
-            handleSaveAndReDeploy();
-          } else {
-            handleSave();
-          }
-        }}
-        status={buttonStatus}
-      >
-        Save build settings
-      </Button>
-    </>
-  );
-};
-
-export default BuildSettingsTab;

+ 0 - 261
dashboard/src/main/home/app-dashboard/build-settings/DetectDockerfileAndPorterYaml.tsx

@@ -1,261 +0,0 @@
-import React, { useState, useEffect, useContext, useCallback } from "react";
-import styled from "styled-components";
-import Button from "components/porter/Button";
-import api from "shared/api";
-import Error from "components/porter/Error";
-
-import { Context } from "shared/Context";
-import { FileType } from "shared/types";
-
-import Spacer from "components/porter/Spacer";
-import Modal from "components/porter/Modal";
-import Input from "components/porter/Input";
-import Text from "components/porter/Text";
-import Link from "components/porter/Link";
-import { PorterApp } from "../types/porterApp";
-
-type PropsType = {
-  setPorterYaml: (yaml: string, filename: string) => void;
-  porterApp: PorterApp;
-  updatePorterApp: (attrs: Partial<PorterApp>) => void;
-  updateDockerfileFound: () => void;
-  setBuildpackView: () => void;
-};
-
-const DetectDockerfileAndPorterYaml: React.FC<PropsType> = ({
-  setPorterYaml,
-  porterApp,
-  updatePorterApp,
-  updateDockerfileFound,
-  setBuildpackView,
-}) => {
-  const [showModal, setShowModal] = useState(false);
-  const [loading, setLoading] = useState(true);
-  const [error, setError] = useState(false);
-  const [contents, setContents] = useState<FileType[]>([]);
-  const [buttonStatus, setButtonStatus] = useState<React.ReactNode>("");
-  const [possiblePorterYamlPath, setPossiblePorterYamlPath] = useState<string>("");
-
-  const { currentProject } = useContext(Context);
-  const fetchAndSetPorterYaml = async (fileName: string) => {
-    setButtonStatus("loading");
-    const response = await fetchPorterYamlContent(fileName);
-    if (response == null) {
-      setButtonStatus(<Error message="Unable to detect porter.yaml. Please check your path and try again, or continue without using porter.yaml." />);
-    } else {
-      setPorterYaml(atob(response.data), fileName);
-      setButtonStatus("success");
-    }
-    setShowModal(false);
-  };
-
-  useEffect(() => {
-    const fetchOnRender = async () => {
-      try {
-        const response = await fetchPorterYamlContent("./porter.yaml");
-        setPorterYaml(atob(response.data), "./porter.yaml");
-      } catch (error) {
-        setShowModal(true);
-      }
-    };
-    fetchOnRender();
-  }, []);
-
-  useEffect(() => {
-    updateContents();
-  }, []);
-
-  useEffect(() => {
-    const dockerFileItem = contents.find((item: FileType) =>
-      item.path.includes("Dockerfile")
-    );
-
-    if (dockerFileItem) {
-      const path = dockerFileItem.path.startsWith("./") || dockerFileItem.path.startsWith("/") ? dockerFileItem.path : `./${dockerFileItem.path}`;
-      updatePorterApp({ dockerfile: path });
-      updateDockerfileFound();
-    } else {
-      setBuildpackView();
-    }
-  }, [contents]);
-
-  const renderContentList = () => {
-    contents.map((item: FileType, i: number) => {
-      let splits = item.path.split("/");
-      let fileName = splits[splits.length - 1];
-      if (fileName.includes("Dockerfile")) {
-        return false;
-      }
-    });
-
-    return true;
-  };
-
-  const fetchContents = () => {
-    if (currentProject == null) {
-      return;
-    }
-
-    return api.getBranchContents(
-      "<token>",
-      { dir: porterApp.build_context || "./" },
-      {
-        project_id: currentProject.id,
-        git_repo_id: porterApp.git_repo_id,
-        kind: "github",
-        owner: porterApp.repo_name.split("/")[0],
-        name: porterApp.repo_name.split("/")[1],
-        branch: porterApp.git_branch,
-      }
-    );
-  };
-
-  const fetchPorterYamlContent = async (porterYamlPath: string) => {
-    try {
-      if (currentProject == null) {
-        return;
-      }
-      const res = await api.getPorterYamlContents(
-        "<token>",
-        {
-          path: porterYamlPath,
-        },
-        {
-          project_id: currentProject.id,
-          git_repo_id: porterApp.git_repo_id,
-          kind: "github",
-          owner: porterApp.repo_name.split("/")[0],
-          name: porterApp.repo_name.split("/")[1],
-          branch: porterApp.git_branch,
-        }
-      );
-      return res;
-    } catch (err) {
-      // console.log(err);
-    }
-
-  };
-
-  const updateContents = async () => {
-    try {
-      const res = await fetchContents();
-      let files = [] as FileType[];
-      let folders = [] as FileType[];
-      res.data.map((x: FileType, i: number) => {
-        x.type === "dir" ? folders.push(x) : files.push(x);
-      });
-
-      folders.sort((a: FileType, b: FileType) => {
-        return a.path < b.path ? 1 : 0;
-      });
-      files.sort((a: FileType, b: FileType) => {
-        return a.path < b.path ? 1 : 0;
-      });
-      let contents = folders.concat(files);
-
-      setContents(contents);
-      setLoading(false);
-      setError(false);
-    } catch (err) {
-      console.log(err);
-      setLoading(false);
-      setError(true);
-    }
-  };
-
-  const NoPorterYamlContent = () => (
-    <div>
-      <Text size={16}>No <Code>porter.yaml</Code> detected</Text>
-      <Spacer y={0.5} />
-      <span>
-        <Text color="helper">
-          We were unable to find a <Code>porter.yaml</Code> file in your root directory. We
-          recommend that you add a <Code>porter.yaml</Code> file to your root directory
-          or specify the path here.
-        </Text>
-        <Spacer y={0.5} />
-        <Link
-          to="https://docs.porter.run/deploy/configuration-as-code/overview"
-          target="_blank"
-          hasunderline
-        >
-          Using porter.yaml
-        </Link>
-      </span>
-    </div>
-  );
-  return (
-    <>
-      {showModal && (
-        <Modal closeModal={() => setShowModal(false)}>
-          <NoPorterYamlContent />
-          <Spacer y={0.5} />
-          <Text color="helper">Path to <Code>porter.yaml</Code> from repository root:</Text>
-          <Spacer y={0.5} />
-          <Input
-            disabled={false}
-            placeholder="ex: ./subdirectory/porter.yaml"
-            value={possiblePorterYamlPath}
-            width="100%"
-            setValue={setPossiblePorterYamlPath}
-          />
-          <Spacer y={1} />
-          <div style={{ display: "flex", justifyContent: "space-between" }}>
-            <Button
-              onClick={() => {
-                setShowModal(false);
-                updatePorterApp({ porter_yaml_path: "" });
-              }}
-              loadingText="Submitting..."
-              color="#ffffff11"
-              status={loading ? "loading" : undefined}
-            >
-              Ignore
-            </Button>
-            <Button
-              onClick={() => fetchAndSetPorterYaml(possiblePorterYamlPath)}
-              loadingText="Submitting..."
-              color="#616fee"
-              status={loading ? "loading" : undefined}
-            >
-              Update path
-            </Button>
-          </div>
-        </Modal>
-      )}
-      {renderContentList() && (
-        <>
-          {possiblePorterYamlPath !== "" && (
-            <>
-              <Text color="helper">Porter.yaml path:</Text>
-              <Spacer y={0.5} />
-              <Input
-                disabled={false}
-                placeholder="ex: ./"
-                value={possiblePorterYamlPath}
-                width="100%"
-                onValueChange={setPossiblePorterYamlPath}
-              />
-              <Spacer y={1} />
-              <Button
-                onClick={() => fetchAndSetPorterYaml(possiblePorterYamlPath)}
-                loadingText="Submitting..."
-                status={buttonStatus}
-              >
-                Update Path
-              </Button>
-              <Spacer y={1} />
-            </>
-          )}
-
-        </>
-      )}
-    </>
-  );
-};
-
-export default DetectDockerfileAndPorterYaml;
-
-const Code = styled.span`
-  font-family: monospace;
-`;

+ 0 - 195
dashboard/src/main/home/app-dashboard/build-settings/SharedBuildSettings.tsx

@@ -1,195 +0,0 @@
-import Input from "components/porter/Input";
-import Spacer from "components/porter/Spacer";
-import Text from "components/porter/Text";
-import React from "react";
-import styled from "styled-components";
-import { BuildMethod, PorterApp } from "../types/porterApp";
-import DetectDockerfileAndPorterYaml from "./DetectDockerfileAndPorterYaml";
-import RepositorySelector from "./RepositorySelector";
-import BranchSelector from "./BranchSelector";
-import AdvancedBuildSettings from "./AdvancedBuildSettings";
-
-type Props = {
-  setPorterYaml: (yaml: string, filename: string) => void;
-  updatePorterApp: (attrs: Partial<PorterApp>) => void;
-  porterApp: PorterApp;
-  autoDetectionOn: boolean;
-  canChangeRepo: boolean;
-  buildView: BuildMethod;
-  setBuildView: (buildView: BuildMethod) => void;
-};
-
-const SharedBuildSettings: React.FC<Props> = ({
-  setPorterYaml,
-  updatePorterApp,
-  porterApp,
-  autoDetectionOn,
-  canChangeRepo,
-  buildView,
-  setBuildView,
-}) => {
-  return (
-    <>
-      <Text size={16}>Build settings</Text>
-      <Spacer y={0.5} />
-      <Text color="helper">Specify your GitHub repository.</Text>
-      <Spacer y={0.5} />
-      {porterApp.repo_name === "" && (
-        <>
-          <ExpandedWrapper>
-            <RepositorySelector
-              readOnly={false}
-              updatePorterApp={updatePorterApp}
-              git_repo_name={porterApp.repo_name}
-            />
-          </ExpandedWrapper>
-          <DarkMatter antiHeight="-4px" />
-          <Spacer y={0.3} />
-        </>
-      )}
-      {porterApp.repo_name !== "" && (
-        <>
-          <Input
-            disabled={true}
-            label="GitHub repository:"
-            width="100%"
-            value={porterApp.repo_name}
-            setValue={() => { }}
-            placeholder=""
-          />
-          {canChangeRepo &&
-            <>
-              <BackButton
-                width="135px"
-                onClick={() => {
-                  updatePorterApp({
-                    repo_name: "",
-                    git_branch: "",
-                    dockerfile: "",
-                    build_context: "./",
-                    porter_yaml_path: "./porter.yaml",
-                  })
-                }}
-              >
-                <i className="material-icons">keyboard_backspace</i>
-                Select repo
-              </BackButton>
-              <Spacer y={0.5} />
-            </>
-          }
-          <Spacer y={0.5} />
-          <Text color="helper">Specify your GitHub branch.</Text>
-          <Spacer y={0.5} />
-          {porterApp.git_branch === "" && (
-            <>
-              <ExpandedWrapper>
-                <BranchSelector
-                  setBranch={(branch: string) => updatePorterApp({ git_branch: branch })}
-                  repo_name={porterApp.repo_name}
-                  git_repo_id={porterApp.git_repo_id}
-                />
-              </ExpandedWrapper>
-            </>
-          )}
-          {porterApp.git_branch !== "" && (
-            <>
-              <Input
-                disabled={true}
-                label="GitHub branch:"
-                type="text"
-                width="100%"
-                value={porterApp.git_branch}
-                setValue={() => { }}
-                placeholder=""
-              />
-              <BackButton
-                width="145px"
-                onClick={() => {
-                  updatePorterApp({
-                    git_branch: "",
-                    dockerfile: "",
-                    build_context: "./",
-                    porter_yaml_path: "./porter.yaml",
-                  })
-                }}
-              >
-                <i className="material-icons">keyboard_backspace</i>
-                Select branch
-              </BackButton>
-              <Spacer y={1} />
-              <Text color="helper">Specify your application root path.</Text>
-              <Spacer y={0.5} />
-              <Input
-                placeholder="ex: ./"
-                value={porterApp.build_context}
-                width="100%"
-                setValue={(val: string) => updatePorterApp({ build_context: val })}
-              />
-              <Spacer y={1} />
-              {/* TODO: refactor everything from the below 'component' into this file */}
-              {autoDetectionOn && (
-                <DetectDockerfileAndPorterYaml
-                  setPorterYaml={setPorterYaml}
-                  porterApp={porterApp}
-                  updatePorterApp={updatePorterApp}
-                  updateDockerfileFound={() => setBuildView("docker")}
-                  setBuildpackView={() => setBuildView("buildpacks")}
-                />
-              )}
-              <AdvancedBuildSettings
-                porterApp={porterApp}
-                updatePorterApp={updatePorterApp}
-                autoDetectBuildpacks={autoDetectionOn}
-                buildView={buildView}
-                setBuildView={setBuildView}
-              />
-            </>
-          )}
-        </>
-      )}
-    </>
-  );
-};
-
-export default SharedBuildSettings;
-
-const DarkMatter = styled.div<{ antiHeight?: string }>`
-  width: 100%;
-  margin-top: ${(props) => props.antiHeight || "-15px"};
-`;
-
-const ExpandedWrapper = styled.div`
-  margin-top: 10px;
-  width: 100%;
-  border-radius: 3px;
-  max-height: 275px;
-`;
-
-const BackButton = styled.div`
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  margin-top: 22px;
-  cursor: pointer;
-  font-size: 13px;
-  height: 35px;
-  padding: 5px 13px;
-  margin-bottom: -7px;
-  padding-right: 15px;
-  border: 1px solid #ffffff55;
-  border-radius: 100px;
-  width: ${(props: { width: string }) => props.width};
-  color: white;
-  background: #ffffff11;
-
-  :hover {
-    background: #ffffff22;
-  }
-
-  > i {
-    color: white;
-    font-size: 16px;
-    margin-right: 6px;
-  }
-`;
-

+ 0 - 158
dashboard/src/main/home/app-dashboard/build-settings/buildpacks/AddCustomBuildpackComponent.tsx

@@ -1,158 +0,0 @@
-import InputRow from "components/form-components/InputRow";
-import React, { useState } from "react";
-import styled, { keyframes } from "styled-components";
-import { Buildpack } from "../../types/buildpack";
-
-function isValidBuildpack(url: string): boolean {
-  const urnPrefix = "urn:cnb:registry:";
-  if (url.startsWith(urnPrefix)) {
-    return true;
-  }
-  try {
-    new URL(url);
-    return true;
-  } catch (error) {
-    return false;
-  }
-}
-
-const AddCustomBuildpackComponent: React.FC<{
-  onAdd: (buildpack: Buildpack) => void;
-}> = ({ onAdd }) => {
-  const [buildpackUrl, setBuildpackUrl] = useState("");
-  const [error, setError] = useState(false);
-
-  const handleAddCustomBuildpack = () => {
-    if (buildpackUrl === "" || !isValidBuildpack(buildpackUrl)) {
-      setError(true);
-      return;
-    }
-    setBuildpackUrl("");
-    onAdd({
-      buildpack: buildpackUrl,
-      name: buildpackUrl,
-      config: {},
-    });
-  };
-
-  return (
-    <StyledCard marginBottom="0px">
-      <ContentContainer>
-        <EventInformation>
-          <BuildpackInputContainer>
-            GitHub or ZIP URL
-            <BuildpackUrlInput
-              placeholder="https://github.com/custom/buildpack"
-              type="input"
-              value={buildpackUrl}
-              isRequired
-              setValue={(newUrl) => {
-                setError(false);
-                setBuildpackUrl(newUrl as string);
-              }}
-            />
-            <ErrorText hasError={error}>Please enter a valid url</ErrorText>
-          </BuildpackInputContainer>
-        </EventInformation>
-      </ContentContainer>
-      <ActionContainer>
-        <ActionButton onClick={() => handleAddCustomBuildpack()}>
-          <span className="material-icons-outlined">add</span>
-        </ActionButton>
-      </ActionContainer>
-    </StyledCard>
-  );
-};
-
-export default AddCustomBuildpackComponent;
-
-const fadeIn = keyframes`
-  from {
-    opacity: 0;
-  }
-  to {
-    opacity: 1;
-  }
-`;
-
-const StyledCard = styled.div<{ marginBottom?: string }>`
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  border: 1px solid #494b4f;
-  background: ${({ theme }) => theme.fg};
-  margin-bottom: ${(props) => props.marginBottom || "30px"};
-  border-radius: 8px;
-  padding: 14px;
-  overflow: hidden;
-  height: 60px;
-  font-size: 13px;
-  animation: ${fadeIn} 0.5s;
-`;
-
-const ContentContainer = styled.div`
-  display: flex;
-  height: 100%;
-  width: 100%;
-  align-items: center;
-`;
-
-const EventInformation = styled.div`
-  display: flex;
-  flex-direction: column;
-  justify-content: space-around;
-  height: 100%;
-`;
-
-const BuildpackInputContainer = styled.div`
-  font-family: "Work Sans", sans-serif;
-  font-weight: 500;
-  color: #ffffff;
-  padding-left: 15px;
-`;
-
-const BuildpackUrlInput = styled(InputRow)`
-  width: auto;
-  min-width: 300px;
-  max-width: 600px;
-  margin: unset;
-  margin-left: 10px;
-  display: inline-block;
-`;
-
-const ErrorText = styled.span`
-  color: red;
-  margin-left: 10px;
-  display: ${(props: { hasError: boolean }) =>
-    props.hasError ? "inline-block" : "none"};
-`;
-
-const ActionContainer = styled.div`
-  display: flex;
-  align-items: center;
-  white-space: nowrap;
-  height: 100%;
-`;
-
-const ActionButton = styled.button`
-  position: relative;
-  border: none;
-  background: none;
-  color: white;
-  padding: 5px;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  border-radius: 50%;
-  cursor: pointer;
-  color: #aaaabb;
-
-  :hover {
-    background: #ffffff11;
-    border: 1px solid #ffffff44;
-  }
-
-  > span {
-    font-size: 20px;
-  }
-`;

+ 0 - 168
dashboard/src/main/home/app-dashboard/build-settings/buildpacks/BuildpackCard.tsx

@@ -1,168 +0,0 @@
-import React, { useMemo } from "react";
-import { DeviconsNameList } from "assets/devicons-name-list";
-import styled, { keyframes } from "styled-components";
-import { Draggable } from "react-beautiful-dnd";
-import { Buildpack } from "main/home/app-dashboard/types/buildpack";
-
-interface Props {
-  buildpack: Buildpack;
-  action: "add" | "remove";
-  onClickFn: (buildpack: string) => void;
-  index: number;
-  draggable: boolean;
-}
-
-const BuildpackCard: React.FC<Props> = ({
-  buildpack,
-  action,
-  onClickFn,
-  index,
-  draggable,
-}) => {
-  const iconClassName = useMemo(() => {
-    if (!buildpack.name) {
-      return "";
-    }
-
-    const splits = buildpack.name.split("/");
-    if (splits.length !== 1) {
-      return "";
-    }
-
-    const devicon = DeviconsNameList.find(
-      (devicon) => splits[0].toLowerCase() === devicon.name
-    );
-    if (!devicon) {
-      return "";
-    }
-    return `devicon-${devicon.name}-plain colored`
-  }, [buildpack.name]);
-
-  const renderedBuildpackName = useMemo(() => {
-    return buildpack.name ?? buildpack.buildpack;
-  }, [buildpack.name]);
-
-  return draggable ? (
-    <Draggable draggableId={renderedBuildpackName} index={index} key={renderedBuildpackName}>
-      {(provided) => (
-        <StyledCard
-          marginBottom="5px"
-          {...provided.draggableProps}
-          {...provided.dragHandleProps}
-          ref={provided.innerRef}
-          key={renderedBuildpackName}
-        >
-          <ContentContainer>
-            {iconClassName && <Icon className={iconClassName} />}
-            <EventInformation>
-              <EventName>{renderedBuildpackName}</EventName>
-            </EventInformation>
-          </ContentContainer>
-          <ActionContainer>
-            <ActionButton onClick={() => onClickFn(buildpack.buildpack)}>
-              <span className="material-icons">
-                {action === "remove" ? "delete" : "add"}
-              </span>
-            </ActionButton>
-          </ActionContainer>
-        </StyledCard>
-      )}
-    </Draggable>
-  ) : (
-    <StyledCard marginBottom="5px" key={renderedBuildpackName}>
-      <ContentContainer>
-        {iconClassName && <Icon className={iconClassName} />}
-        <EventInformation>
-          <EventName>{renderedBuildpackName}</EventName>
-        </EventInformation>
-      </ContentContainer>
-      <ActionContainer>
-        <ActionButton onClick={() => onClickFn(buildpack.buildpack)}>
-          <span className="material-icons">
-            {action === "remove" ? "delete" : "add"}
-          </span>
-        </ActionButton>
-      </ActionContainer>
-    </StyledCard>
-  );
-};
-
-export default BuildpackCard;
-
-const fadeIn = keyframes`
-  from {
-    opacity: 0;
-  }
-  to {
-    opacity: 1;
-  }
-`;
-
-const StyledCard = styled.div<{ marginBottom?: string }>`
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  border: 1px solid #494b4f;
-  background: ${({ theme }) => theme.fg};
-  margin-bottom: ${(props) => props.marginBottom || "30px"};
-  border-radius: 8px;
-  padding: 14px;
-  overflow: hidden;
-  height: 60px;
-  font-size: 13px;
-  animation: ${fadeIn} 0.5s;
-`;
-
-const ContentContainer = styled.div`
-  display: flex;
-  height: 100%;
-  width: 100%;
-  align-items: center;
-`;
-
-const Icon = styled.span`
-  font-size: 20px;
-  margin-left: 10px;
-  margin-right: 20px
-`;
-
-const EventInformation = styled.div`
-  display: flex;
-  flex-direction: column;
-  justify-content: space-around;
-  height: 100%;
-`;
-
-const EventName = styled.div`
-  font-family: "Work Sans", sans-serif;
-  font-weight: 500;
-  color: #ffffff;
-`;
-
-const ActionContainer = styled.div`
-  display: flex;
-  align-items: center;
-  white-space: nowrap;
-  height: 100%;
-`;
-
-const ActionButton = styled.button`
-  position: relative;
-  border: none;
-  background: none;
-  color: white;
-  padding: 5px;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  border-radius: 50%;
-  cursor: pointer;
-  color: #aaaabb;
-  :hover {
-    background: #ffffff11;
-    border: 1px solid #ffffff44;
-  }
-  > span {
-    font-size: 20px;
-  }
-`;

+ 0 - 145
dashboard/src/main/home/app-dashboard/build-settings/buildpacks/BuildpackConfigurationModal.tsx

@@ -1,145 +0,0 @@
-import Spacer from 'components/porter/Spacer';
-import Text from 'components/porter/Text';
-import React from 'react';
-import BuildpackList from './BuildpackList';
-import AddCustomBuildpackComponent from './AddCustomBuildpackComponent';
-import Icon from 'components/porter/Icon';
-import Button from 'components/porter/Button';
-import Modal from 'components/porter/Modal';
-import styled from 'styled-components';
-import Select from 'components/porter/Select';
-import stars from "assets/stars-white.svg";
-import { Buildpack } from '../../types/buildpack';
-import { PorterApp } from '../../types/porterApp';
-
-interface Props {
-    closeModal: () => void;
-    selectedStack: string;
-    sortedStackOptions: { value: string; label: string }[];
-    setStackValue: (value: string) => void;
-    selectedBuildpacks: Buildpack[];
-    setSelectedBuildpacks: (buildpacks: Buildpack[]) => void;
-    availableBuildpacks: Buildpack[];
-    setAvailableBuildpacks: (buildpacks: Buildpack[]) => void;
-    porterApp: PorterApp;
-    updatePorterApp: (attrs: Partial<PorterApp>) => void;
-    isDetectingBuildpacks: boolean;
-    detectBuildpacksError: string;
-    handleAddCustomBuildpack: (buildpack: Buildpack) => void;
-    detectAndSetBuildPacks: (detect: boolean) => void;
-}
-const BuildpackConfigurationModal: React.FC<Props> = ({
-    closeModal,
-    selectedStack,
-    sortedStackOptions,
-    setStackValue,
-    selectedBuildpacks,
-    setSelectedBuildpacks,
-    availableBuildpacks,
-    setAvailableBuildpacks,
-    porterApp,
-    updatePorterApp,
-    isDetectingBuildpacks,
-    detectBuildpacksError,
-    handleAddCustomBuildpack,
-    detectAndSetBuildPacks,
-}) => {
-    return (
-        <Modal closeModal={closeModal}>
-            <Text size={16}>Buildpack Configuration</Text>
-            <Spacer y={1} />
-            <Scrollable>
-                <Text>Builder:</Text>
-                {selectedStack === "" &&
-                    <>
-                        <Spacer y={0.5} />
-                        <Text color="helper">
-                            No builder detected. Click 'Detect buildpacks' below to scan your repository for available builders and buildpacks.
-                        </Text>
-                    </>
-                }
-                {selectedStack !== "" &&
-                    <>
-                        <Spacer y={0.5} />
-                        <Select
-                            value={selectedStack}
-                            width="300px"
-                            options={sortedStackOptions}
-                            setValue={setStackValue}
-                        />
-                    </>
-                }
-                <BuildpackList
-                    selectedBuildpacks={selectedBuildpacks}
-                    setSelectedBuildpacks={setSelectedBuildpacks}
-                    availableBuildpacks={availableBuildpacks}
-                    setAvailableBuildpacks={setAvailableBuildpacks}
-                    porterApp={porterApp}
-                    updatePorterApp={updatePorterApp}
-                    showAvailableBuildpacks={true}
-                    isDetectingBuildpacks={isDetectingBuildpacks}
-                    detectBuildpacksError={detectBuildpacksError}
-                    droppableId={"modal"}
-                />
-                <Spacer y={0.5} />
-                <Text>
-                    Custom buildpacks
-                </Text>
-                <Spacer y={0.5} />
-                <Text color="helper">
-                    You may also add buildpacks by directly providing their GitHub links
-                    or links to ZIP files that contain the buildpack source code.
-                </Text>
-                <Spacer y={1} />
-                <AddCustomBuildpackComponent onAdd={handleAddCustomBuildpack} />
-                <Spacer y={2} />
-            </Scrollable>
-            <Footer>
-                <Shade />
-                <FooterButtons>
-                    <Button onClick={() => detectAndSetBuildPacks(true)}>
-                        <Icon src={stars} height="15px" />
-                        <Spacer inline x={0.5} />
-                        Detect buildpacks
-                    </Button>
-                    <Button onClick={closeModal} width={"75px"}>Close</Button>
-                </FooterButtons>
-            </Footer>
-        </Modal>
-    );
-}
-export default BuildpackConfigurationModal;
-
-const Scrollable = styled.div`
-  overflow-y: auto;
-  padding: 0 25px;
-  width: calc(100% + 50px);
-  margin-left: -25px;
-  max-height: calc(100vh - 300px);
-`;
-
-const FooterButtons = styled.div`
-  display: flex;
-  justify-content: space-between;
-`;
-
-const Footer = styled.div`
-  position: relative;
-  width: calc(100% + 50px);
-  margin-left: -25px;
-  padding: 0 25px;
-  border-bottom-left-radius: 10px;
-  border-bottom-right-radius: 10px;
-  background: ${({ theme }) => theme.fg};
-  margin-bottom: -30px;
-  padding-bottom: 30px;
-`;
-
-const Shade = styled.div`
-  position: absolute;
-  top: -50px;
-  left: 0;
-  height: 50px;
-  width: 100%;
-  background: linear-gradient(to bottom, #00000000, ${({ theme }) => theme.fg});
-`;

+ 0 - 142
dashboard/src/main/home/app-dashboard/build-settings/buildpacks/BuildpackList.tsx

@@ -1,142 +0,0 @@
-import React from "react";
-import { PorterApp } from "../../types/porterApp";
-import BuildpackCard from "./BuildpackCard";
-import Spacer from "components/porter/Spacer";
-import Text from "components/porter/Text";
-import Loading from "components/Loading";
-import Error from "components/porter/Error";
-import { Droppable, DragDropContext } from "react-beautiful-dnd";
-import { Buildpack } from "../../types/buildpack";
-
-interface Props {
-    porterApp: PorterApp,
-    updatePorterApp: (attrs: Partial<PorterApp>) => void,
-    selectedBuildpacks: Buildpack[],
-    availableBuildpacks: Buildpack[],
-    setSelectedBuildpacks: (buildpacks: Buildpack[]) => void,
-    setAvailableBuildpacks: (buildpacks: Buildpack[]) => void,
-    showAvailableBuildpacks: boolean,
-    isDetectingBuildpacks: boolean,
-    detectBuildpacksError: string,
-    droppableId: string,
-}
-const BuildpackList: React.FC<Props> = ({
-    porterApp,
-    updatePorterApp,
-    selectedBuildpacks,
-    availableBuildpacks,
-    setSelectedBuildpacks,
-    setAvailableBuildpacks,
-    showAvailableBuildpacks,
-    isDetectingBuildpacks,
-    detectBuildpacksError,
-    droppableId,
-}) => {
-    const handleRemoveBuildpack = (buildpackToRemove: string) => {
-        if (porterApp.buildpacks.includes(buildpackToRemove)) {
-            updatePorterApp({ buildpacks: porterApp.buildpacks.filter(bp => bp !== buildpackToRemove) });
-            const buildpack = selectedBuildpacks.find(bp => bp.buildpack === buildpackToRemove) as Buildpack;
-            if (buildpack != null) {
-                setAvailableBuildpacks([...availableBuildpacks, buildpack]);
-                setSelectedBuildpacks(selectedBuildpacks.filter(bp => bp.buildpack !== buildpackToRemove));
-            }
-        }
-    };
-
-    const handleAddBuildpack = (buildpackToAdd: string) => {
-        if (porterApp.buildpacks.find((bp) => bp === buildpackToAdd) == null) {
-            updatePorterApp({ buildpacks: [...porterApp.buildpacks, buildpackToAdd] });
-            const buildpack = availableBuildpacks.find((bp) => bp.buildpack === buildpackToAdd);
-            if (buildpack != null) {
-                setSelectedBuildpacks([...selectedBuildpacks, buildpack]);
-                setAvailableBuildpacks(availableBuildpacks.filter((bp) => bp.buildpack !== buildpackToAdd));
-            }
-        }
-    };
-
-    const onDragEnd = (result: any) => {
-        if (!result.destination) {
-            return;
-        }
-        const oldSelected = [...selectedBuildpacks];
-        const [removed] = oldSelected.splice(result.source.index, 1);
-        oldSelected.splice(result.destination.index, 0, removed);
-        setSelectedBuildpacks(oldSelected);
-        updatePorterApp({ buildpacks: oldSelected.map((bp) => bp.buildpack) });
-    };
-
-    const renderAvailableBuildpacks = () => {
-        if (isDetectingBuildpacks) {
-            return (
-                <Loading />
-            )
-        }
-
-        if (detectBuildpacksError) {
-            return (
-                <Error message={detectBuildpacksError} />
-            )
-        }
-
-        if (availableBuildpacks.length > 0) {
-            return availableBuildpacks.map((buildpack, index) => {
-                return (
-                    <BuildpackCard
-                        buildpack={buildpack}
-                        action={"add"}
-                        onClickFn={handleAddBuildpack}
-                        index={index}
-                        draggable={false}
-                    />
-                )
-            })
-        }
-
-        return <Text color="helper">No available buildpacks detected.</Text>
-    }
-
-    return (
-        <DragDropContext onDragEnd={onDragEnd}>
-            {showAvailableBuildpacks &&
-                <>
-                    <Spacer y={0.5} />
-                    <Text>Selected buildpacks:</Text>
-                    <Spacer y={0.5} />
-                </>
-            }
-            {selectedBuildpacks.length !== 0 && <Droppable droppableId={droppableId}>
-                {provided => (
-                    <div
-                        {...provided.droppableProps}
-                        ref={provided.innerRef}
-                    >
-                        {selectedBuildpacks.map((buildpack, index) => (
-                            <BuildpackCard
-                                buildpack={buildpack}
-                                action={"remove"}
-                                onClickFn={handleRemoveBuildpack}
-                                index={index}
-                                draggable={true}
-                                key={index}
-                            />
-                        ))}
-                        {provided.placeholder}
-                    </div>
-                )}
-            </Droppable>}
-            {selectedBuildpacks.length === 0 &&
-                <Text color="helper">No buildpacks selected.</Text>
-            }
-            {showAvailableBuildpacks &&
-                <>
-                    <Spacer y={0.5} />
-                    <Text>Available buildpacks:</Text>
-                    <Spacer y={0.5} />
-                    {renderAvailableBuildpacks()}
-                </>
-            }
-        </DragDropContext>
-    );
-};
-
-export default BuildpackList;

+ 0 - 226
dashboard/src/main/home/app-dashboard/build-settings/buildpacks/BuildpackSettings.tsx

@@ -1,226 +0,0 @@
-import Helper from "components/form-components/Helper";
-import React, { useContext, useEffect, useMemo, useState } from "react";
-import api from "shared/api";
-import { Context } from "shared/Context";
-import styled, { keyframes } from "styled-components";
-import Button from "components/porter/Button";
-import Spacer from "components/porter/Spacer";
-import Error from "components/porter/Error";
-import { PorterApp } from "../../types/porterApp";
-import BuildpackList from "./BuildpackList";
-import {
-  BUILDPACK_TO_NAME,
-  BuildConfig,
-  Buildpack,
-  DEFAULT_BUILDER_NAME,
-  DEFAULT_HEROKU_STACK,
-  DetectedBuildpack
-} from "../../types/buildpack";
-import BuildpackConfigurationModal from "./BuildpackConfigurationModal";
-
-const BuildpackSettings: React.FC<{
-  porterApp: PorterApp;
-  updatePorterApp: (attrs: Partial<PorterApp>) => void;
-  autoDetectBuildpacks: boolean;
-}> = ({
-  porterApp,
-  updatePorterApp,
-  autoDetectBuildpacks,
-}) => {
-    const { currentProject } = useContext(Context);
-
-    const [selectedStack, setSelectedStack] = useState<string>("");
-    const [stackOptions, setStackOptions] = useState<{ label: string; value: string }[]>([]);
-    const [isModalOpen, setIsModalOpen] = useState(false);
-    const [isDetectingBuildpacks, setIsDetectingBuildpacks] = useState(false);
-    const [error, setError] = useState<string>("");
-
-    const [selectedBuildpacks, setSelectedBuildpacks] = useState<Buildpack[]>([]);
-    const [availableBuildpacks, setAvailableBuildpacks] = useState<Buildpack[]>([]);
-
-    const detectAndSetBuildPacks = async (detect: boolean) => {
-      try {
-        if (currentProject == null) {
-          return;
-        }
-
-        if (!detect) {
-          // in this case, we are not detecting buildpacks, so we just populate based on the DB
-          if (porterApp.builder != null) {
-            setSelectedStack(porterApp.builder);
-            setStackOptions([{ label: porterApp.builder, value: porterApp.builder }]);
-          }
-          if (porterApp.buildpacks != null) {
-            setSelectedBuildpacks(porterApp.buildpacks.map(bp => ({
-              name: BUILDPACK_TO_NAME[bp] ?? bp,
-              buildpack: bp,
-              config: {},
-            })));
-          }
-        } else {
-          if (isDetectingBuildpacks) {
-            return;
-          }
-          setIsDetectingBuildpacks(true);
-          const detectBuildPackRes = await api.detectBuildpack(
-            "<token>",
-            {
-              dir: porterApp.build_context || ".",
-            },
-            {
-              project_id: currentProject.id,
-              git_repo_id: porterApp.git_repo_id,
-              kind: "github",
-              owner: porterApp.repo_name.split("/")[0],
-              name: porterApp.repo_name.split("/")[1],
-              branch: porterApp.git_branch,
-            }
-          );
-
-          const builders = detectBuildPackRes.data as DetectedBuildpack[];
-          if (builders.length === 0) {
-            return;
-          }
-          setStackOptions(builders.flatMap((builder) => {
-            return builder.builders.map((stack) => ({
-              label: `${builder.name} - ${stack}`,
-              value: stack.toLowerCase(),
-            }));
-          }).sort((a, b) => {
-            if (a.label < b.label) {
-              return -1;
-            }
-            if (a.label > b.label) {
-              return 1;
-            }
-            return 0;
-          }));
-
-          const defaultBuilder = builders.find(
-            (builder) => builder.name.toLowerCase() === DEFAULT_BUILDER_NAME
-          ) ?? builders[0];
-
-          const allBuildpacks = defaultBuilder.others.concat(defaultBuilder.detected);
-
-          let detectedBuilder: string;
-          if (defaultBuilder.builders.length && defaultBuilder.builders.includes(DEFAULT_HEROKU_STACK)) {
-            setSelectedStack(DEFAULT_HEROKU_STACK);
-            detectedBuilder = DEFAULT_HEROKU_STACK;
-          } else {
-            setSelectedStack(defaultBuilder.builders[0]);
-            detectedBuilder = defaultBuilder.builders[0];
-          }
-
-          const newBuildpacks = defaultBuilder.detected.filter(bp => !porterApp.buildpacks.includes(bp.buildpack));
-          if (autoDetectBuildpacks) {
-            updatePorterApp({ builder: detectedBuilder, buildpacks: [...porterApp.buildpacks, ...newBuildpacks.map(bp => bp.buildpack)] });
-            setSelectedBuildpacks(defaultBuilder.detected);
-            setAvailableBuildpacks(defaultBuilder.others);
-            setError("");
-          } else {
-            updatePorterApp({ builder: detectedBuilder });
-            setAvailableBuildpacks(allBuildpacks.filter(bp => !porterApp.buildpacks?.includes(bp.buildpack)));
-          }
-        }
-      } catch (err) {
-        setError(`Unable to detect buildpacks at path: ${porterApp.build_context}. Please make sure your repo, branch, and application root path are all set correctly and attempt to detect again.`);
-      } finally {
-        setIsDetectingBuildpacks(false);
-      }
-    }
-
-    useEffect(() => {
-      detectAndSetBuildPacks(autoDetectBuildpacks);
-    }, [currentProject]);
-
-    const handleAddCustomBuildpack = (buildpack: Buildpack) => {
-      if (porterApp.buildpacks.find((bp) => bp === buildpack.buildpack) == null) {
-        updatePorterApp({ buildpacks: [...porterApp.buildpacks, buildpack.buildpack] });
-        setSelectedBuildpacks([...selectedBuildpacks, buildpack]);
-      }
-    };
-
-    return (
-      <BuildpackConfigurationContainer>
-        {selectedBuildpacks.length > 0 && (
-          <>
-            <Helper>
-              The following buildpacks were automatically detected. You can also
-              manually add, remove, or re-order buildpacks here.
-            </Helper>
-            <BuildpackList
-              selectedBuildpacks={selectedBuildpacks}
-              setSelectedBuildpacks={setSelectedBuildpacks}
-              availableBuildpacks={availableBuildpacks}
-              setAvailableBuildpacks={setAvailableBuildpacks}
-              porterApp={porterApp}
-              updatePorterApp={updatePorterApp}
-              showAvailableBuildpacks={false}
-              isDetectingBuildpacks={isDetectingBuildpacks}
-              detectBuildpacksError={error}
-              droppableId={"non-modal"}
-            />
-          </>
-        )}
-        {autoDetectBuildpacks && error !== "" && (
-          <>
-            <Spacer y={1} />
-            <Error message={error} />
-          </>
-        )}
-        <Spacer y={1} />
-        <Button onClick={() => {
-          setIsModalOpen(true);
-          setError("");
-        }}>
-          <I className="material-icons">add</I> Add / detect buildpacks
-        </Button>
-        {isModalOpen && (
-          <BuildpackConfigurationModal
-            closeModal={() => setIsModalOpen(false)}
-            selectedStack={selectedStack}
-            sortedStackOptions={stackOptions}
-            setStackValue={(option) => {
-              setSelectedStack(option);
-              updatePorterApp({ builder: option });
-            }}
-            selectedBuildpacks={selectedBuildpacks}
-            setSelectedBuildpacks={setSelectedBuildpacks}
-            availableBuildpacks={availableBuildpacks}
-            setAvailableBuildpacks={setAvailableBuildpacks}
-            porterApp={porterApp}
-            updatePorterApp={updatePorterApp}
-            isDetectingBuildpacks={isDetectingBuildpacks}
-            detectBuildpacksError={error}
-            handleAddCustomBuildpack={handleAddCustomBuildpack}
-            detectAndSetBuildPacks={detectAndSetBuildPacks}
-          />
-        )}
-      </BuildpackConfigurationContainer>
-    );
-  };
-
-export default BuildpackSettings;
-
-
-const I = styled.i`
-  color: white;
-  font-size: 14px;
-  display: flex;
-  align-items: center;
-  margin-right: 5px;
-  justify-content: center;
-`;
-
-const fadeIn = keyframes`
-  from {
-    opacity: 0;
-  }
-  to {
-    opacity: 1;
-  }
-`;
-
-const BuildpackConfigurationContainer = styled.div`
-  animation: ${fadeIn} 0.75s;
-`;

+ 0 - 128
dashboard/src/main/home/app-dashboard/create-app/PorterYamlModal.tsx

@@ -1,128 +0,0 @@
-import React, { useState } from "react";
-import styled from "styled-components";
-
-import Link from "components/porter/Link";
-import Spacer from "components/porter/Spacer";
-import Modal from "components/porter/Modal";
-import Text from "components/porter/Text";
-import Input from "components/porter/Input";
-import Button from "components/porter/Button";
-import FileSelector from "../validate-apply/build-settings/FileSelector";
-
-type Props = {
-    close: () => void;
-    setPorterYamlPath: (path: string) => void;
-    porterYamlPath: string;
-    projectId: number;
-    repoId: number;
-    repoOwner: string;
-    repoName: string;
-    branch: string;
-}
-
-const PorterYamlModal: React.FC<Props> = ({ 
-    close, 
-    setPorterYamlPath, 
-    porterYamlPath,
-    projectId,
-    repoId,
-    repoOwner,
-    repoName,
-    branch, 
-}) => {
-    const [possiblePorterYamlPath, setPossiblePorterYamlPath] = useState<string>("");
-    const [showModal, setShowModal] = useState<boolean>(true);
-    const [showFileSelector, setShowFileSelector] = useState<boolean>(false);
-
-    return showModal ? (
-        <Modal closeModal={() => { setShowModal(false); }}>
-            <div>
-                <Text size={16}>No <Code>porter.yaml</Code> detected at <Code>{porterYamlPath}</Code></Text>
-                <Spacer y={0.5} />
-                <span>
-                    <Text color="helper">
-                        We were unable to find a <Code>porter.yaml</Code> file in your repository.
-                    </Text>
-                    <Spacer y={0.5} />
-                    <Text color="helper">
-                        Although not required, we
-                        recommend that you add a <Code>porter.yaml</Code> file to the root of your repository,
-                        or you may specify its path here.
-                    </Text>
-                    <Spacer y={0.5} />
-                    <Link
-                        to="https://docs.porter.run/deploy/configuration-as-code/overview"
-                        target="_blank"
-                        hasunderline
-                    >
-                        Using porter.yaml
-                    </Link>
-                </span>
-            </div>
-            <Spacer y={0.5} />
-            <Text color="helper">Path to <Code>porter.yaml</Code> from repository root:</Text>
-            <Spacer y={0.5} />
-            <InputWrapper
-                onClick={(e) => {
-                    e.stopPropagation();
-                    if (!showFileSelector) {
-                        setShowFileSelector(true);
-                        setPossiblePorterYamlPath("");
-                    }
-                }}
-            >
-                <Input
-                    placeholder="ex: ./subdirectory/porter.yaml"
-                    value={possiblePorterYamlPath}
-                    width="100%"
-                    setValue={setPossiblePorterYamlPath}
-                    hideCursor={true}
-                />
-            </InputWrapper>
-            {showFileSelector && 
-                <div>
-                    <FileSelector 
-                        projectId={projectId} 
-                        repoId={repoId} 
-                        repoOwner={repoOwner} 
-                        repoName={repoName} 
-                        branch={branch}
-                        onFileSelect={(path: string) => { setPossiblePorterYamlPath(`./${path}`); }} 
-                        isFileSelectable={(path: string) => path.endsWith(".yaml")}
-                        headerText={"Select your porter.yaml:"}
-                    />
-                </div>
-            }
-            <Spacer y={1} />
-            <div style={{ display: "flex", justifyContent: "space-between" }}>
-                <Button
-                    onClick={close}
-                    color="#ffffff11"
-                >
-                    Ignore
-                </Button>
-                <Button
-                    onClick={() => {
-                        setPorterYamlPath(possiblePorterYamlPath);
-                        setShowModal(false);
-                    }}
-                    disabled={possiblePorterYamlPath === ""}
-                >
-                    Confirm path
-                </Button>
-            </div>
-        </Modal>
-    ) : null;
-};
-
-export default PorterYamlModal;
-
-const Code = styled.span`
-  font-family: monospace;
-`;
-
-const InputWrapper = styled.div`
-    width: 500px;
-    display: flex;
-    justify-content: space-between;
-`;

+ 0 - 224
dashboard/src/main/home/app-dashboard/new-app-flow/GithubConnectModal.tsx

@@ -1,224 +0,0 @@
-import { RouteComponentProps, withRouter } from "react-router";
-import styled from "styled-components";
-import React, { useEffect, useState } from "react";
-
-import Modal from "components/porter/Modal";
-import Text from "components/porter/Text";
-import Spacer from "components/porter/Spacer";
-import ExpandableSection from "components/porter/ExpandableSection";
-import Fieldset from "components/porter/Fieldset";
-import Button from "components/porter/Button";
-
-import api from "shared/api";
-import Error from "components/porter/Error";
-
-import Helper from "components/form-components/Helper";
-import github from "assets/github-white.png";
-
-type Props = RouteComponentProps & {
-  closeModal: () => void;
-  hasClickedDoNotConnect: boolean;
-  handleDoNotConnect: () => void;
-  setAccessError: (error: boolean) => void;
-  setAccessLoading: (loading: boolean) => void;
-  setAccessData: (data: GithubAppAccessData) => void;
-  accessData: GithubAppAccessData;
-  accessError: boolean;
-};
-
-interface GithubAppAccessData {
-  username?: string;
-  accounts?: string[];
-  accessError?: boolean;
-}
-
-const GithubConnectModal: React.FC<Props> = ({
-  closeModal,
-  hasClickedDoNotConnect,
-  handleDoNotConnect,
-  accessError,
-  setAccessError,
-  setAccessLoading,
-  setAccessData,
-  accessData,
-}) => {
-  const [loading, setLoading] = React.useState<boolean>(false);
-  const url = `${window.location.protocol}//${window.location.host}${window.location.pathname}`;
-  const encoded_redirect_uri = encodeURIComponent(url);
-
-  const renderGithubConnect = () => {
-    const url = `${window.location.protocol}//${window.location.host}${window.location.pathname}`;
-    const encoded_redirect_uri = encodeURIComponent(url);
-
-    if (accessError) {
-      return (
-        <>
-          <Text color="helper">To deploy from GitHub, authorize Porter to view your repos.</Text>
-          <ListWrapper>
-            <Helper>
-              No connected repos found.
-              <A href={"/api/integrations/github-app/oauth"}>
-                Authorize Porter to view your repos.
-              </A>
-            </Helper>
-          </ListWrapper>
-          <Spacer y={1} />
-          <Button
-            onClick={handleDoNotConnect}
-            loadingText="Submitting..."
-            color="#ffffff11"
-            status={loading ? "loading" : undefined}
-          >
-            Dismiss
-          </Button>
-        </>
-      );
-    } else if (!accessData.accounts || accessData.accounts?.length == 0) {
-      return (
-        <>
-          <Text color="helper">
-            You are currently authorized as <B>{accessData.username}</B>.
-          </Text>
-          <Spacer y={1} />
-          <ConnectToGithubButton
-            href={`/api/integrations/github-app/install?redirect_uri=${encoded_redirect_uri}`}
-            target="_blank"
-            rel="noopener noreferrer"
-            onClick={closeModal}
-          >
-            <GitHubIcon src={github} />
-            Install the Porter GitHub app
-          </ConnectToGithubButton>
-          <Spacer y={1} />
-          <Button
-            onClick={handleDoNotConnect}
-            loadingText="Submitting..."
-            color="#ffffff11"
-            withBorder
-            status={loading ? "loading" : undefined}
-          >
-            Dismiss
-          </Button>
-        </>
-      );
-    }
-  };
-  useEffect(() => {
-    api
-      .getGithubAccounts("<token>", {}, {})
-      .then(({ data }) => {
-        setAccessData(data);
-        setAccessLoading(false);
-      })
-      .catch(() => {
-        setAccessError(true);
-        setAccessLoading(false);
-      });
-  }, []);
-  return (
-    !hasClickedDoNotConnect &&
-    (accessError ||
-      !accessData.accounts ||
-      accessData.accounts?.length === 0) && (
-      <>
-        <Modal closeModal={closeModal}>
-          <Text size={16}>
-            <GitIcon src={github} />
-            Configure GitHub
-          </Text>
-          <Spacer y={0.5} />
-          {renderGithubConnect()}
-        </Modal>
-      </>
-    )
-  );
-};
-
-export default withRouter(GithubConnectModal);
-
-const B = styled.b`
-  display: inline;
-  color: #ffffff;
-  margin-left: 5px;
-`;
-
-const GitIcon = styled.img`
-  width: 15px;
-  height: 15px;
-  opacity: 0.9;
-  margin-right: 10px;
-  filter: brightness(120%);
-`;
-
-const ListWrapper = styled.div`
-  width: 100%;
-  height: 240px;
-  background: #ffffff11;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  border-radius: 5px;
-  margin-top: 20px;
-  padding: 40px;
-`;
-const A = styled.a`
-  color: #8590ff;
-  text-decoration: underline;
-  margin-left: 5px;
-  cursor: pointer;
-`;
-
-const ConnectToGithubButton = styled.a`
-  width: 240px;
-  justify-content: center;
-  border-radius: 5px;
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-  font-size: 13px;
-  cursor: pointer;
-  font-family: "Work Sans", sans-serif;
-  color: white;
-  font-weight: 500;
-  padding: 10px;
-  overflow: hidden;
-  white-space: nowrap;
-  border: 1px solid #494b4f;
-  text-overflow: ellipsis;
-  cursor: ${(props: { disabled?: boolean }) =>
-    props.disabled ? "not-allowed" : "pointer"};
-
-  background: ${(props: { disabled?: boolean }) =>
-    props.disabled ? "#aaaabbee" : "#2E3338"};
-  :hover {
-    background: ${(props: { disabled?: boolean }) =>
-    props.disabled ? "" : "#353a3e"};
-  }
-
-  > i {
-    color: white;
-    width: 18px;
-    height: 18px;
-    font-weight: 600;
-    font-size: 12px;
-    border-radius: 20px;
-    display: flex;
-    align-items: center;
-    margin-right: 5px;
-    justify-content: center;
-  }
-  &:hover {
-    background: ${(props: { disabled?: boolean }) =>
-    props.disabled ? "" : "#353a3e"};
-  }
-
-  &:not([disabled]) {
-    cursor: pointer;
-  }
-`;
-
-const GitHubIcon = styled.img`
-  width: 20px;
-  filter: brightness(150%);
-  margin-right: 10px;
-`;

+ 0 - 97
dashboard/src/main/home/app-dashboard/new-app-flow/SourceSettings.tsx

@@ -1,97 +0,0 @@
-import AnimateHeight from "react-animate-height";
-import React from "react";
-import Spacer from "components/porter/Spacer";
-import styled from "styled-components";
-import { SourceType } from "./SourceSelector";
-import { RouteComponentProps, withRouter } from "react-router";
-import SharedBuildSettings from "../build-settings/SharedBuildSettings";
-import { BuildMethod, PorterApp } from "../types/porterApp";
-import ImageSettings from "../image-settings/ImageSettings";
-
-type Props = RouteComponentProps & {
-  source: SourceType | undefined;
-  imageUrl: string;
-  setImageUrl: (x: string) => void;
-  imageTag: string;
-  setImageTag: (x: string) => void;
-  setPorterYaml: (yaml: string, filename: string) => void;
-  porterApp: PorterApp;
-  setPorterApp: React.Dispatch<React.SetStateAction<PorterApp>>;
-  buildView: BuildMethod;
-  setBuildView: (buildView: BuildMethod) => void;
-  projectId: number;
-  resetImageInfo: () => void;
-};
-
-const SourceSettings: React.FC<Props> = ({
-  source,
-  imageUrl,
-  setImageUrl,
-  imageTag,
-  setImageTag,
-  setPorterYaml,
-  porterApp,
-  setPorterApp,
-  buildView,
-  setBuildView,
-  projectId,
-  resetImageInfo,
-}) => {
-  return (
-    <SourceSettingsContainer>
-      <AnimateHeight height={source ? "auto" : 0}>
-        <Spacer y={1} />
-        {source === "github" ? (
-          <SharedBuildSettings
-            setPorterYaml={setPorterYaml}
-            porterApp={porterApp}
-            updatePorterApp={(attrs: Partial<PorterApp>) => setPorterApp((prev: PorterApp) => PorterApp.setAttributes(prev, attrs))}
-            autoDetectionOn={true}
-            canChangeRepo={true}
-            buildView={buildView}
-            setBuildView={setBuildView}
-          />
-        ) :
-          <ImageSettings
-            projectId={projectId}
-            imageTag={imageTag}
-            setImageTag={setImageTag}
-            imageUri={imageUrl}
-            setImageUri={setImageUrl}
-            resetImageInfo={resetImageInfo}
-          />
-        }
-      </AnimateHeight>
-    </SourceSettingsContainer>
-  );
-};
-
-export default withRouter(SourceSettings);
-
-const SourceSettingsContainer = styled.div``;
-
-const DarkMatter = styled.div<{ antiHeight?: string }>`
-  width: 100%;
-  margin-top: ${(props) => props.antiHeight || "-15px"};
-`;
-
-const Subtitle = styled.div`
-  padding: 11px 0px 16px;
-  font-family: "Work Sans", sans-serif;
-  font-size: 13px;
-  color: #aaaabb;
-  line-height: 1.6em;
-`;
-
-const StyledSourceBox = styled.div`
-  width: 100%;
-  color: #ffffff;
-  padding: 14px 35px 20px;
-  position: relative;
-  font-size: 13px;
-  margin-top: 6px;
-  margin-bottom: 25px;
-  border-radius: 5px;
-  background: ${(props) => props.theme.fg};
-  border: 1px solid #494b4f;
-`;

+ 0 - 144
dashboard/src/main/home/app-dashboard/new-app-flow/schema.tsx

@@ -1,144 +0,0 @@
-import { KeyValueType } from "main/home/cluster-dashboard/env-groups/EnvGroupArray";
-import * as z from "zod";
-import { JobService, ReleaseService, Service, WebService, WorkerService } from "./serviceTypes";
-import { overrideObjectValues } from "./utils";
-import _ from "lodash";
-
-const appConfigSchema = z.object({
-    run: z.string().min(1),
-    config: z.any().optional(),
-    type: z.enum(['web', 'worker', 'job']).optional(),
-});
-
-export const AppsSchema = z.record(appConfigSchema);
-
-export const EnvSchema = z.record(z.string());
-
-export const BuildSchema = z.object({
-    method: z.string().refine(value => ["pack", "docker", "registry"].includes(value)),
-    context: z.string().optional(),
-    builder: z.string().optional(),
-    buildpacks: z.array(z.string()).optional(),
-    dockerfile: z.string().optional(),
-    image: z.string().optional()
-}).refine(value => {
-    if (value.method === "pack") {
-        return value.builder != null;
-    }
-    if (value.method === "docker") {
-        return value.dockerfile != null;
-    }
-    if (value.method === "registry") {
-        return value.image != null;
-    }
-    return false;
-},
-    { message: "Invalid build configuration" });
-
-
-export const PorterYamlSchema = z.object({
-    version: z.string().optional(),
-    build: BuildSchema.optional(),
-    env: EnvSchema.optional(),
-    apps: AppsSchema,
-    release: appConfigSchema.optional(),
-});
-
-export const createFinalPorterYaml = (
-    services: Service[],
-    dashboardSetEnvVariables: KeyValueType[],
-    porterJson: PorterJson | undefined,
-    injectPortEnvVariable: boolean = false,
-): PorterJson => {
-    const [apps, port] = createApps(services.filter(Service.isNonRelease), porterJson, injectPortEnvVariable);
-    const env = combineEnv(dashboardSetEnvVariables, porterJson?.env);
-
-    // inject a port env variable if necessary
-    if (port != null) {
-        env.PORT = port;
-    }
-
-    const release = services.find(Service.isRelease);
-
-    return release != null && !_.isEmpty(release.startCommand.value) ? {
-        version: "v1stack",
-        env,
-        apps,
-        release: createRelease(release, porterJson),
-    } : {
-        version: "v1stack",
-        env,
-        apps,
-    };
-};
-
-const combineEnv = (
-    dashboardSetVariables: KeyValueType[],
-    porterYamlSetVariables: Record<string, string> | undefined
-): z.infer<typeof EnvSchema> => {
-    const env: z.infer<typeof EnvSchema> = {};
-    for (const { key, value } of dashboardSetVariables) {
-        env[key] = value;
-    }
-    if (porterYamlSetVariables != null) {
-        for (const [key, value] of Object.entries(porterYamlSetVariables)) {
-            env[key] = value;
-        }
-    }
-    return env;
-};
-
-const createApps = (
-    serviceList: (WorkerService | WebService | JobService)[],
-    porterJson: PorterJson | undefined,
-    injectPortEnvVariable: boolean,
-): [z.infer<typeof AppsSchema>, string | undefined] => {
-    const apps: z.infer<typeof AppsSchema> = {};
-    let port: string | undefined = undefined;
-    for (const service of serviceList) {
-        let config = Service.serialize(service);
-
-        if (
-            porterJson != null &&
-            porterJson.apps != null &&
-            porterJson.apps[service.name] != null &&
-            porterJson.apps[service.name].config != null
-        ) {
-            config = overrideObjectValues(
-                config,
-                porterJson.apps[service.name].config
-            );
-        }
-
-        if (injectPortEnvVariable && service.type === "web") {
-            port = service.port.value;
-        }
-
-        apps[service.name] = {
-            type: service.type,
-            run: service.startCommand.value,
-            config,
-        };
-    }
-
-    return [apps, port];
-};
-
-const createRelease = (release: ReleaseService, porterJson: PorterJson | undefined): z.infer<typeof appConfigSchema> => {
-    let config = Service.serialize(release);
-
-    if (porterJson?.release?.config != null) {
-        config = overrideObjectValues(
-            config,
-            porterJson.release.config
-        );
-    }
-
-    return {
-        type: 'job',
-        run: release.startCommand.value,
-        config,
-    }
-}
-
-export type PorterJson = z.infer<typeof PorterYamlSchema>;

+ 0 - 237
dashboard/src/main/home/app-dashboard/validate-apply/app-settings/ExpandableEnvGroup.tsx

@@ -1,237 +0,0 @@
-import React, { useState } from "react";
-import styled, { keyframes } from "styled-components";
-import { type PopulatedEnvGroup } from "./types";
-import Spacer from "components/porter/Spacer";
-import Icon from "components/porter/Icon";
-
-type Props = {
-  index: number;
-  remove: (index: number) => void;
-  envGroup: PopulatedEnvGroup;
-  icon: JSX.Element;
-};
-
-const ExpandableEnvGroup: React.FC<Props> = ({ index, remove, envGroup, icon }) => {
-  const [isExpanded, setIsExpanded] = useState(false);
-
-  return (
-    <StyledCard>
-      <Flex>
-        {icon}
-        <Spacer inline x={1} />
-        <ContentContainer>
-          <EventInformation>
-            <EventName>{envGroup.name}</EventName>
-          </EventInformation>
-        </ContentContainer>
-        <ActionContainer>
-          <ActionButton type="button" onClick={() => { remove(index); }}>
-            <span className="material-icons">delete</span>
-          </ActionButton>
-          <ActionButton
-            type="button"
-            onClick={() => { setIsExpanded((prev) => !prev); }}
-          >
-            <i className="material-icons">
-              {isExpanded ? "arrow_drop_up" : "arrow_drop_down"}
-            </i>
-          </ActionButton>
-        </ActionContainer>
-      </Flex>
-      {isExpanded ? (
-        <>
-          {Object.entries(envGroup.variables ?? {}).map(([key, value], i) => (
-            <InputWrapper key={i}>
-              <KeyInput
-                placeholder="ex: key"
-                width="270px"
-                value={key}
-                disabled
-              />
-              <Spacer x={0.5} inline />
-              <MultiLineInput
-                placeholder="ex: value"
-                width="270px"
-                value={value}
-                disabled
-                rows={value.split("\n").length}
-                spellCheck={false}
-              />
-            </InputWrapper>
-          ))}
-          {Object.entries(envGroup.secret_variables ?? {}).map(
-            ([key, value], i) => (
-              <InputWrapper key={i}>
-                <KeyInput
-                  placeholder="ex: key"
-                  width="270px"
-                  value={key}
-                  disabled
-                />
-                <Spacer x={0.5} inline />
-                <KeyInput
-                  placeholder="ex: value"
-                  width="270px"
-                  value={value}
-                  disabled
-                  type="password"
-                />
-              </InputWrapper>
-            )
-          )}
-        </>
-      ) : null}
-    </StyledCard>
-  );
-};
-
-export default ExpandableEnvGroup;
-
-const fadeIn = keyframes`
-  from {
-    opacity: 0;
-  }
-  to {
-    opacity: 1;
-  }
-`;
-
-const StyledCard = styled.div`
-  border: 1px solid #ffffff44;
-  background: #ffffff11;
-  border-radius: 8px;
-  padding: 10px 14px;
-  overflow: hidden;
-  font-size: 13px;
-  animation: ${fadeIn} 0.5s;
-`;
-
-const Flex = styled.div`
-  display: flex;
-  height: 25px;
-  align-items: center;
-  justify-content: space-between;
-`;
-
-const ContentContainer = styled.div`
-  display: flex;
-  height: 40px;
-  width: 100%;
-  align-items: center;
-`;
-
-const EventInformation = styled.div`
-  display: flex;
-  flex-direction: column;
-  justify-content: space-around;
-  height: 100%;
-`;
-
-const EventName = styled.div`
-  font-family: "Work Sans", sans-serif;
-  font-weight: 500;
-  color: #ffffff;
-`;
-
-const ActionContainer = styled.div`
-  display: flex;
-  align-items: center;
-  white-space: nowrap;
-  height: 100%;
-`;
-
-const ActionButton = styled.button`
-  position: relative;
-  border: none;
-  background: none;
-  color: white;
-  padding: 5px;
-  width: 30px;
-  height: 30px;
-  margin-left: 5px;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  border-radius: 50%;
-  cursor: pointer;
-  color: #aaaabb;
-  border: 1px solid #ffffff00;
-
-  :hover {
-    background: #ffffff11;
-    border: 1px solid #ffffff44;
-  }
-
-  > span {
-    font-size: 20px;
-  }
-`;
-
-const InputWrapper = styled.div`
-  display: flex;
-  align-items: center;
-  margin-top: 5px;
-`;
-
-type InputProps = {
-  disabled?: boolean;
-  width: string;
-  borderColor?: string;
-};
-
-const KeyInput = styled.input<InputProps>`
-  outline: none;
-  border: none;
-  margin-bottom: 5px;
-  font-size: 13px;
-  background: #ffffff11;
-  border: 1px solid
-    ${(props) => (props.borderColor ? props.borderColor : "#ffffff55")};
-  border-radius: 3px;
-  width: ${(props) => (props.width ? props.width : "270px")};
-  color: ${(props) => (props.disabled ? "#ffffff44" : "white")};
-  padding: 5px 10px;
-  height: 35px;
-`;
-
-export const MultiLineInput = styled.textarea<InputProps>`
-  outline: none;
-  border: none;
-  margin-bottom: 5px;
-  font-size: 13px;
-  background: #ffffff11;
-  border: 1px solid
-    ${(props) => (props.borderColor ? props.borderColor : "#ffffff55")};
-  border-radius: 3px;
-  min-width: ${(props) => (props.width ? props.width : "270px")};
-  max-width: ${(props) => (props.width ? props.width : "270px")};
-  color: ${(props) => (props.disabled ? "#ffffff44" : "white")};
-  padding: 8px 10px 5px 10px;
-  min-height: 35px;
-  max-height: 100px;
-  white-space: nowrap;
-
-  ::-webkit-scrollbar {
-    width: 8px;
-    :horizontal {
-      height: 8px;
-    }
-  }
-
-  ::-webkit-scrollbar-corner {
-    width: 10px;
-    background: #ffffff11;
-    color: white;
-  }
-
-  ::-webkit-scrollbar-track {
-    width: 10px;
-    -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
-    box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
-  }
-
-  ::-webkit-scrollbar-thumb {
-    background-color: darkgrey;
-    outline: 1px solid slategrey;
-  }
-`;

+ 0 - 189
dashboard/src/main/home/app-dashboard/validate-apply/services-settings/tabs/Old_GPUResources.tsx

@@ -1,189 +0,0 @@
-import React, { useContext, useState } from "react";
-import { Switch } from "@material-ui/core";
-import { Controller, useFormContext } from "react-hook-form";
-import styled from "styled-components";
-
-import Loading from "components/Loading";
-import Container from "components/porter/Container";
-import InputSlider from "components/porter/InputSlider";
-import Link from "components/porter/Link";
-import Spacer from "components/porter/Spacer";
-import Tag from "components/porter/Tag";
-import Text from "components/porter/Text";
-import ProvisionClusterModal from "main/home/sidebar/ProvisionClusterModal";
-import { type PorterAppFormData } from "lib/porter-apps";
-
-import { Context } from "shared/Context";
-import addCircle from "assets/add-circle.png";
-import infra from "assets/cluster.svg";
-
-type Props = {
-  clusterContainsGPUNodes: boolean;
-  maxGPU: number;
-  index: number;
-};
-
-// TODO: delete this file and all references once new infra tab is GA
-const OldGPUResources: React.FC<Props> = ({
-  clusterContainsGPUNodes,
-  maxGPU,
-  index,
-}) => {
-  const { currentCluster } = useContext(Context);
-  const [clusterModalVisible, setClusterModalVisible] =
-    useState<boolean>(false);
-
-  const { control, watch } = useFormContext<PorterAppFormData>();
-  const gpu = watch(`app.services.${index}.gpu.enabled`, {
-    readOnly: false,
-    value: false,
-  });
-  return (
-    <>
-      <Spacer y={1} />
-      <Controller
-        name={`app.services.${index}.gpu`}
-        control={control}
-        render={({ field: { value, onChange } }) => (
-          <>
-            <Container row>
-              <Switch
-                size="small"
-                color="primary"
-                checked={value.enabled.value}
-                disabled={!clusterContainsGPUNodes}
-                onChange={() => {
-                  onChange({
-                    ...value,
-                    enabled: {
-                      ...value.enabled,
-                      value: !value.enabled.value,
-                    },
-                    gpuCoresNvidia: {
-                      ...value.gpuCoresNvidia,
-                      value: value.enabled.value ? 0 : 1,
-                    },
-                  });
-                }}
-                inputProps={{ "aria-label": "controlled" }}
-              />
-              <Spacer inline x={0.5} />
-              <Text>
-                <>
-                  <span>Enable GPU</span>
-                </>
-              </Text>
-
-              {!clusterContainsGPUNodes && (
-                <>
-                  <Spacer inline x={1} />
-                  <Text color="helper">
-                    Your cluster has no GPU nodes available.
-                  </Text>
-                  <Spacer inline x={0.5} />
-                  {currentCluster?.status !== "UPDATING" && (
-                    <Tag>
-                      <Link
-                        onClick={() => {
-                          setClusterModalVisible(true);
-                        }}
-                      >
-                        <TagIcon src={addCircle} />
-                        Add GPU nodes
-                      </Link>
-                    </Tag>
-                  )}
-                </>
-              )}
-            </Container>
-
-            <Spacer y={0.5} />
-            {clusterModalVisible && (
-              <ProvisionClusterModal
-                closeModal={() => {
-                  setClusterModalVisible(false);
-                }}
-                gpuModal={true}
-                gcp={currentCluster?.cloud_provider === "GCP"}
-                azure={currentCluster?.cloud_provider === "Azure"}
-              />
-            )}
-          </>
-        )}
-      />
-      {maxGPU > 1 && gpu.value && (
-        <>
-          <Spacer y={1} />
-          <Controller
-            name={`app.services.${index}.gpu`}
-            control={control}
-            render={({ field: { value, onChange } }) => (
-              <InputSlider
-                label="GPU"
-                unit=""
-                min={0}
-                max={maxGPU}
-                value={(value?.gpuCoresNvidia.value ?? 1).toString()}
-                disabled={value?.gpuCoresNvidia.readOnly}
-                setValue={(e) => {
-                  onChange({
-                    ...value,
-                    gpuCoresNvidia: {
-                      ...value.gpuCoresNvidia,
-                      value: e,
-                    },
-                  });
-                }}
-                disabledTooltip={
-                  "You may only edit this field in your porter.yaml."
-                }
-              />
-            )}
-          />
-        </>
-      )}
-      {currentCluster?.status === "UPDATING" && !clusterContainsGPUNodes && (
-        <CheckItemContainer>
-          <CheckItemTop>
-            <Loading offset="0px" width="20px" height="20px" />
-            <Spacer inline x={1} />
-            <Text>{"Cluster is updating..."}</Text>
-            <Spacer inline x={1} />
-            <Tag>
-              <Link to={`/cluster-dashboard`}>
-                <TagIcon src={infra} />
-                View Status
-              </Link>
-            </Tag>
-          </CheckItemTop>
-        </CheckItemContainer>
-      )}
-    </>
-  );
-};
-
-export default OldGPUResources;
-
-const CheckItemContainer = styled.div`
-  display: flex;
-  flex-direction: column;
-  border: 1px solid ${(props) => props.theme.border};
-  border-radius: 5px;
-  font-size: 13px;
-  width: 100%;
-  margin-bottom: 10px;
-  padding-left: 10px;
-  background: ${(props) => props.theme.clickable.bg};
-`;
-
-const CheckItemTop = styled.div`
-  display: flex;
-  align-items: center;
-  padding: 10px;
-  background: ${(props) => props.theme.clickable.bg};
-`;
-
-const TagIcon = styled.img`
-  height: 12px;
-  margin-right: 3px;
-`;

+ 0 - 181
dashboard/src/main/home/app-dashboard/validate-apply/services-settings/tabs/utils.ts

@@ -1,181 +0,0 @@
-export const MIB_TO_GIB = 1024;
-export const MILI_TO_CORE = 1000;
-type InstanceDetails = {
-  vCPU: number;
-  RAM: number;
-  GPU?: number;
-};
-
-type InstanceTypes = Record<string, Record<string, InstanceDetails>>;
-type AzureInstanceTypes = Record<string, InstanceDetails>;
-
-// use values from AWS as base constant, convert to MB
-export const AWS_INSTANCE_LIMITS: InstanceTypes = Object.freeze({
-  t3a: {
-    nano: { vCPU: 2, RAM: 0.5 },
-    micro: { vCPU: 2, RAM: 1 },
-    small: { vCPU: 2, RAM: 2 },
-    medium: { vCPU: 2, RAM: 4 },
-    large: { vCPU: 2, RAM: 8 },
-    xlarge: { vCPU: 4, RAM: 16 },
-    "2xlarge": { vCPU: 8, RAM: 32 },
-  },
-  t3: {
-    nano: { vCPU: 2, RAM: 0.5 },
-    micro: { vCPU: 2, RAM: 1 },
-    small: { vCPU: 2, RAM: 2 },
-    medium: { vCPU: 2, RAM: 4 },
-    large: { vCPU: 2, RAM: 8 },
-    xlarge: { vCPU: 4, RAM: 16 },
-    "2xlarge": { vCPU: 8, RAM: 32 },
-  },
-  t2: {
-    nano: { vCPU: 1, RAM: 0.5 },
-    micro: { vCPU: 1, RAM: 1 },
-    small: { vCPU: 1, RAM: 2 },
-    medium: { vCPU: 2, RAM: 4 },
-    large: { vCPU: 2, RAM: 8 },
-    xlarge: { vCPU: 4, RAM: 16 },
-    "2xlarge": { vCPU: 8, RAM: 32 },
-  },
-  t4g: {
-    nano: { vCPU: 2, RAM: 0.5 },
-    micro: { vCPU: 2, RAM: 1 },
-    small: { vCPU: 2, RAM: 2 },
-    medium: { vCPU: 2, RAM: 4 },
-    large: { vCPU: 2, RAM: 8 },
-    xlarge: { vCPU: 4, RAM: 16 },
-    "2xlarge": { vCPU: 8, RAM: 32 },
-  },
-  c6a: {
-    large: { vCPU: 2, RAM: 4 },
-    xlarge: { vCPU: 4, RAM: 8 },
-    "2xlarge": { vCPU: 8, RAM: 16 },
-    "4xlarge": { vCPU: 16, RAM: 32 },
-    "8xlarge": { vCPU: 32, RAM: 64 },
-  },
-  c6i: {
-    large: { vCPU: 2, RAM: 4 },
-    xlarge: { vCPU: 4, RAM: 8 },
-    "2xlarge": { vCPU: 8, RAM: 16 },
-    "4xlarge": { vCPU: 16, RAM: 32 },
-    "8xlarge": { vCPU: 32, RAM: 64 },
-    "12xlarge": { vCPU: 48, RAM: 96 },
-  },
-  g4dn: {
-    xlarge: { vCPU: 4, RAM: 16 },
-    "2xlarge": { vCPU: 8, RAM: 32 },
-    "4xlarge": { vCPU: 16, RAM: 64 },
-    "8xlarge": { vCPU: 32, RAM: 128 },
-  },
-  r6a: {
-    large: { vCPU: 2, RAM: 16 },
-    xlarge: { vCPU: 4, RAM: 32 },
-    "2xlarge": { vCPU: 8, RAM: 64 },
-    "4xlarge": { vCPU: 16, RAM: 128 },
-    "8xlarge": { vCPU: 32, RAM: 256 },
-  },
-  c5: {
-    large: { vCPU: 2, RAM: 4 },
-    xlarge: { vCPU: 4, RAM: 8 },
-    "2xlarge": { vCPU: 8, RAM: 16 },
-    "4xlarge": { vCPU: 16, RAM: 32 },
-  },
-  m5: {
-    large: { vCPU: 2, RAM: 8 },
-    xlarge: { vCPU: 4, RAM: 16 },
-    "2xlarge": { vCPU: 8, RAM: 32 },
-    "4xlarge": { vCPU: 16, RAM: 64 },
-  },
-  m5n: {
-    large: { vCPU: 2, RAM: 8 },
-    xlarge: { vCPU: 4, RAM: 16 },
-    "2xlarge": { vCPU: 8, RAM: 32 },
-    "4xlarge": { vCPU: 16, RAM: 64 },
-  },
-  m6a: {
-    large: { vCPU: 2, RAM: 8 },
-    xlarge: { vCPU: 4, RAM: 16 },
-    "2xlarge": { vCPU: 8, RAM: 32 },
-    "4xlarge": { vCPU: 16, RAM: 64 },
-    "8xlarge": { vCPU: 32, RAM: 128 },
-    "12xlarge": { vCPU: 48, RAM: 192 },
-  },
-  m7i: {
-    large: { vCPU: 2, RAM: 8 },
-    xlarge: { vCPU: 4, RAM: 16 },
-    "2xlarge": { vCPU: 8, RAM: 32 },
-    "4xlarge": { vCPU: 16, RAM: 64 },
-    "8xlarge": { vCPU: 32, RAM: 128 },
-    "12xlarge": { vCPU: 48, RAM: 192 },
-  },
-  x2gd: {
-    medium: { vCPU: 1, RAM: 16 },
-    large: { vCPU: 2, RAM: 32 },
-    xlarge: { vCPU: 4, RAM: 64 },
-  },
-  m5n: {
-    large: { vCPU: 2, RAM: 8 },
-    xlarge: { vCPU: 4, RAM: 16 },
-    "2xlarge": { vCPU: 8, RAM: 32 },
-    "4xlarge": { vCPU: 16, RAM: 64 },
-  },
-  // add GCP instance tyoes : TO DO add a dedicated section for GCP
-  e2: {
-    "standard-2": { vCPU: 2, RAM: 8 },
-    "standard-4": { vCPU: 4, RAM: 16 },
-    "standard-8": { vCPU: 8, RAM: 32 },
-    "standard-16": { vCPU: 16, RAM: 64 },
-    "standard-32": { vCPU: 32, RAM: 128 },
-    "standard-64": { vCPU: 64, RAM: 256 },
-  },
-  c3: {
-    "highcpu-4": { vCPU: 4, RAM: 8 },
-    "highcpu-8": { vCPU: 8, RAM: 16 },
-    "highcpu-22": { vCPU: 22, RAM: 44 },
-    "highcpu-44": { vCPU: 44, RAM: 88 },
-    "highmem-4": { vCPU: 4, RAM: 32 },
-    "highmem-8": { vCPU: 8, RAM: 64 },
-    "highmem-22": { vCPU: 22, RAM: 176 },
-    "highmem-44": { vCPU: 44, RAM: 352 },
-    "standard-4": { vCPU: 4, RAM: 16 },
-    "standard-8": { vCPU: 8, RAM: 32 },
-    "standard-22": { vCPU: 22, RAM: 88 },
-    "standard-44": { vCPU: 44, RAM: 176 },
-  },
-  c7g: {
-    large: { vCPU: 2, RAM: 4 },
-    xlarge: { vCPU: 4, RAM: 8 },
-    "2xlarge": { vCPU: 8, RAM: 16 },
-    "4xlarge": { vCPU: 16, RAM: 32 },
-    "8xlarge": { vCPU: 32, RAM: 64 },
-    "12xlarge": { vCPU: 48, RAM: 96 },
-    "16xlarge": { vCPU: 64, RAM: 128 },
-  },
-});
-
-export const GPU_INSTANCE_LIMIT: InstanceTypes = Object.freeze({
-  g4dn: {
-    xlarge: { vCPU: 4, RAM: 16, GPU: 1 },
-    "2xlarge": { vCPU: 8, RAM: 32, GPU: 1 },
-  },
-  p4d: {
-    "24xlarge": { vCPU: 96, RAM: 1152, GPU: 8 },
-  },
-  n1: {
-    "standard-1": { vCPU: 1, RAM: 3.75, GPU: 1 },
-    "standard-2": { vCPU: 2, RAM: 7.5, GPU: 1 },
-    "standard-4": { vCPU: 4, RAM: 15, GPU: 1 },
-    "standard-8": { vCPU: 8, RAM: 30, GPU: 1 },
-    "standard-16": { vCPU: 16, RAM: 60, GPU: 1 },
-    "standard-32": { vCPU: 32, RAM: 120, GPU: 1 },
-    "high-mem-2": { vCPU: 2, RAM: 13, GPU: 1 },
-    "high-mem-4": { vCPU: 4, RAM: 26, GPU: 1 },
-    "high-mem-8": { vCPU: 8, RAM: 52, GPU: 1 },
-    "high-mem-16": { vCPU: 16, RAM: 104, GPU: 1 },
-    "high-mem-32": { vCPU: 32, RAM: 208, GPU: 1 },
-    "high-cpu-8": { vCPU: 2, RAM: 1.8, GPU: 1 },
-    "high-cpu-16": { vCPU: 4, RAM: 3.6, GPU: 1 },
-    "high-cpu-32": { vCPU: 8, RAM: 7.2, GPU: 1 },
-  },
-});

+ 0 - 14
dashboard/src/main/home/cluster-dashboard/ClusterPlaceholderContainer.tsx

@@ -1,14 +0,0 @@
-import React, { useContext } from "react";
-
-import { Context } from "shared/Context";
-import ClusterPlaceholder from "./ClusterPlaceholder";
-
-type PropsType = {};
-
-const ClusterPlaceholderContainer: React.FC<PropsType> = () => {
-  const context = useContext(Context);
-
-  return <ClusterPlaceholder currentCluster={context.currentCluster} />;
-}
-
-export default ClusterPlaceholderContainer;

+ 0 - 408
dashboard/src/main/home/cluster-dashboard/dashboard/Compliance.tsx

@@ -1,408 +0,0 @@
-import React, { useContext, useEffect, useMemo, useState } from "react";
-import type { JsonValue } from "@bufbuild/protobuf";
-import { Cluster, Contract, EKS, EKSLogging } from "@porter-dev/api-contracts";
-import axios from "axios";
-import styled from "styled-components";
-import { match } from "ts-pattern";
-
-import Loading from "components/Loading";
-import Button from "components/porter/Button";
-import Container from "components/porter/Container";
-import Error from "components/porter/Error";
-import Fieldset from "components/porter/Fieldset";
-import Spacer from "components/porter/Spacer";
-import Text from "components/porter/Text";
-import ToggleRow from "components/porter/ToggleRow";
-import SOC2Checks from "components/SOC2Checks";
-
-import api from "shared/api";
-import { Context } from "shared/Context";
-import { type Soc2Data } from "shared/types";
-import sparkle from "assets/sparkle.svg";
-
-import DonutChart from "./DonutChart";
-
-type Props = {
-  credentialId: string;
-  provisionerError?: string;
-  selectedClusterVersion: JsonValue;
-};
-
-//  Example SOC2 Check NOTE PLEASE ADD FUNC TO createContract and useEffect(() to correctly READ AND WRITE
-//  "Display_Name_Of_SOC2_Check": {
-//   "message": "Main Example Message about the Check",
-//   "link": "example link for more docs ",
-//   "enabled": (false or true if porter does it by default),
-//   "status": ""(Keep blank or ENABLED if porter does it by default)
-//  "enabledField": "text that goes next to the toggle",
-//   "info": " more information",
-//   "locked":(true if unmutable field like KMS),
-//   "disabledTooltip": "display if message is disabled",
-//  "hideToggle": true (if you want to hide the toggle
-// }
-const soc2DataDefault: Soc2Data = {
-  soc2_checks: {
-    "Public SSH Access": {
-      message:
-        "Porter-provisioned instances do not allow remote SSH access. Users are not allowed to invoke commands directly on the host, and all commands are invoked via the EKS Control Plane.",
-      enabled: true,
-      hideToggle: true,
-      status: "ENABLED",
-    },
-    "Cluster Secret Encryption": {
-      message:
-        "Cluster secrets can be encrypted using an AWS KMS Key. Secrets will be encrypted at rest, and encryption cannot be disabled for secrets.",
-      enabled: false,
-      disabledTooltip:
-        "Enable KMS encryption for the cluster to enable SOC 2 compliance.",
-      link: "https://aws.amazon.com/about-aws/whats-new/2020/03/amazon-eks-adds-envelope-encryption-for-secrets-with-aws-kms/",
-      locked: true,
-      status: "",
-    },
-    "Control Plane Log Retention": {
-      message:
-        "EKS Control Plane logs are by default available for a minimal amount of time, typically 1 hour or less. EKS CloudTrail Forwarding automatically sends control plane logs to CloudTrail for longer retention and later inspection.",
-      enabled: false,
-      enabledField: "Retain CloudTrail logs for 365 days",
-      status: "",
-    },
-    "Enhanced Image Vulnerability Scanning": {
-      message:
-        "AWS ECR scans for CVEs from the open-source Clair database on image push. Enhanced scanning provides continuous, automated scans against images as new vulnerabilities appear.",
-      link: "https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning-enhanced.html",
-      enabled: false,
-      info: "",
-      status: "",
-    },
-  },
-};
-
-const DEFAULT_ERROR_MESSAGE =
-  "An error occurred while provisioning your infrastructure. Please try again.";
-
-const Compliance: React.FC<Props> = (props) => {
-  const { currentProject, currentCluster, setShouldRefreshClusters } =
-    useContext(Context);
-
-  const [isClicked, setIsClicked] = useState(false);
-  const [isLoading, setIsLoading] = useState(false);
-  const [soc2Enabled, setSoc2Enabled] = useState(false);
-  const [clusterRegion, setClusterRegion] = useState("");
-  const [errorMessage, setErrorMessage] = useState<string>("");
-  const [errorDetails, setErrorDetails] = useState<string>("");
-  const [soc2Data, setSoc2Data] = useState(soc2DataDefault);
-  const [isReadOnly, setIsReadOnly] = useState(false);
-
-  const applySettings = async (): Promise<void> => {
-    if (!currentCluster || !currentProject || !setShouldRefreshClusters) {
-      return;
-    }
-
-    try {
-      setIsLoading(true);
-      setIsClicked(true);
-      setIsReadOnly(true);
-
-      const contractResults = await api.getContracts(
-        "<token>",
-        { cluster_id: currentCluster.id },
-        { project_id: currentProject.id }
-      );
-
-      if (contractResults.data.length === 0) {
-        setErrorMessage("Unable to retrieve contract results");
-        setErrorDetails("");
-        return;
-      }
-
-      const result = contractResults.data.reduce(
-        (prev: { CreatedAt: string }, current: { CreatedAt: string }) =>
-          Date.parse(current.CreatedAt) > Date.parse(prev.CreatedAt)
-            ? current
-            : prev
-      );
-
-      const contract = createContract(result.base64_contract);
-
-      await api.createContract("<token>", contract, {
-        project_id: currentProject.id,
-      });
-      setShouldRefreshClusters(true);
-
-      setIsClicked(false);
-      setIsLoading(false);
-      setIsReadOnly(false);
-    } catch (err) {
-      let errMessage =
-        "Failed to provision cluster, please contact support@porter.run.";
-      if (axios.isAxiosError(err) && err.response?.data) {
-        errMessage = err.response.data.error.replace("unknown: ", "");
-      }
-
-      // hacky, need to standardize error contract with backend
-      setIsClicked(false);
-      setIsLoading(false);
-      void markStepStarted("provisioning-failed", errMessage);
-
-      // enable edit again only in the case of an error
-      setIsClicked(false);
-      setIsReadOnly(false);
-    } finally {
-      setIsLoading(false);
-    }
-  };
-
-  const createContract = (base64Contract: string): Contract => {
-    //
-    const cloudTrailEnabled =
-      soc2Data.soc2_checks["Control Plane Log Retention"].enabled;
-    const kmsEnabled =
-      soc2Data.soc2_checks["Cluster Secret Encryption"].enabled;
-    const ecrScanningEnabled =
-      soc2Data.soc2_checks["Enhanced Image Vulnerability Scanning"].enabled;
-
-    const contractData = JSON.parse(atob(base64Contract));
-    const latestCluster: Cluster = Cluster.fromJson(contractData.cluster, {
-      ignoreUnknownFields: true,
-    });
-
-    const updatedKindValues = match(latestCluster.kindValues)
-      .with({ case: "eksKind" }, ({ value }) => ({
-        value: new EKS({
-          ...value,
-          enableKmsEncryption: soc2Enabled || kmsEnabled || false,
-          enableEcrScanning:
-            soc2Enabled ||
-            ecrScanningEnabled ||
-            value.enableEcrScanning ||
-            false,
-          logging: new EKSLogging({
-            enableApiServerLogs: soc2Enabled || cloudTrailEnabled || false,
-            enableAuditLogs: soc2Enabled || cloudTrailEnabled || false,
-            enableAuthenticatorLogs: soc2Enabled || cloudTrailEnabled || false,
-            enableCloudwatchLogsToS3: soc2Enabled || cloudTrailEnabled || false,
-            enableControllerManagerLogs:
-              soc2Enabled || cloudTrailEnabled || false,
-            enableSchedulerLogs: soc2Enabled || cloudTrailEnabled || false,
-          }),
-        }),
-        case: "eksKind" as const,
-      }))
-      .with({ case: "gkeKind" }, ({ value }) => ({
-        value,
-        case: "gkeKind" as const,
-      }))
-      .with({ case: "aksKind" }, ({ value }) => ({
-        value,
-        case: "aksKind" as const,
-      }))
-      .with({ case: undefined }, () => ({
-        value: undefined,
-        case: undefined,
-      }))
-      .exhaustive();
-    const cluster = new Cluster({
-      ...latestCluster,
-      kindValues: updatedKindValues,
-    });
-
-    return new Contract({
-      cluster,
-    });
-  };
-
-  const getStatus = (): JSX.Element | string => {
-    if (isLoading) {
-      return <Loading />;
-    }
-    if (isReadOnly && props.provisionerError === "") {
-      return "Provisioning is still in progress...";
-    } else if (errorMessage !== "") {
-      return (
-        <Error
-          message={
-            errorDetails !== ""
-              ? errorMessage + " (" + errorDetails + ")"
-              : errorMessage
-          }
-          ctaText={
-            errorMessage !== DEFAULT_ERROR_MESSAGE
-              ? "Troubleshooting steps"
-              : undefined
-          }
-        />
-      );
-    }
-    return "";
-  };
-
-  const isDisabled = (): boolean | undefined => {
-    return (
-      isUserProvisioning ||
-      isClicked ||
-      (currentCluster && !currentProject?.enable_reprovision)
-    );
-  };
-
-  const markStepStarted = async (
-    step: string,
-    errMessage?: string
-  ): Promise<void> => {
-    try {
-      await api.updateOnboardingStep(
-        "<token>",
-        {
-          step,
-          error_message: errMessage,
-          region: clusterRegion,
-          provider: "aws",
-        },
-        {
-          project_id: currentProject ? currentProject.id : 0,
-        }
-      );
-    } catch (err) {}
-  };
-
-  const isUserProvisioning = useMemo(() => {
-    return isReadOnly && props.provisionerError === "";
-  }, [isReadOnly, props.provisionerError]);
-
-  const determineStatus = (enabled: boolean): string => {
-    if (enabled) {
-      if (currentCluster?.status === "UPDATING") {
-        return "PENDING_ENABLED";
-      } else return "ENABLED";
-    }
-    return "";
-  };
-
-  useEffect(() => {
-    const contract: Contract = Contract.fromJson(props.selectedClusterVersion, {
-      ignoreUnknownFields: true,
-    });
-
-    if (contract.cluster && contract.cluster.kindValues.case === "eksKind") {
-      const eksValues = contract.cluster.kindValues.value;
-      const cloudTrailEnabled =
-        eksValues.logging != null &&
-        eksValues.logging.enableApiServerLogs &&
-        eksValues.logging.enableAuditLogs &&
-        eksValues.logging.enableAuthenticatorLogs &&
-        eksValues.logging.enableControllerManagerLogs;
-
-      setClusterRegion(eksValues.region);
-
-      setSoc2Data((prevSoc2Data) => {
-        return {
-          ...prevSoc2Data,
-          soc2_checks: {
-            ...prevSoc2Data.soc2_checks,
-            "Control Plane Log Retention": {
-              ...prevSoc2Data.soc2_checks["Control Plane Log Retention"],
-              enabled: cloudTrailEnabled,
-              status: determineStatus(cloudTrailEnabled),
-            },
-            "Cluster Secret Encryption": {
-              ...prevSoc2Data.soc2_checks["Cluster Secret Encryption"],
-              enabled: eksValues.enableKmsEncryption,
-              status: determineStatus(eksValues.enableKmsEncryption),
-            },
-            "Enhanced Image Vulnerability Scanning": {
-              ...prevSoc2Data.soc2_checks[
-                "Enhanced Image Vulnerability Scanning"
-              ],
-              enabled: eksValues.enableEcrScanning,
-              status: determineStatus(eksValues.enableEcrScanning),
-            },
-          },
-        };
-      });
-
-      setSoc2Enabled(
-        cloudTrailEnabled &&
-          eksValues.enableKmsEncryption &&
-          eksValues.enableEcrScanning
-      );
-    }
-  }, [props.selectedClusterVersion]);
-
-  useEffect(() => {
-    if (!currentCluster) {
-      return;
-    }
-
-    setIsReadOnly(
-      currentCluster.status === "UPDATING" ||
-        currentCluster.status === "UPDATING_UNAVAILABLE"
-    );
-  }, []);
-
-  return (
-    <StyledCompliance>
-      <Spacer y={1} />
-      <Fieldset>
-        <Container row>
-          <Text size={16}>SOC 2 Compliance Dashboard</Text>
-          <Spacer inline x={1} />
-          <NewBadge>
-            <img src={sparkle} />
-            New
-          </NewBadge>
-        </Container>
-        <Spacer y={1} />
-        <DonutChart data={soc2Data} />
-      </Fieldset>
-
-      <SOC2Checks
-        enableAll={soc2Enabled}
-        soc2Data={soc2Data}
-        setSoc2Data={setSoc2Data}
-        readOnly={isReadOnly}
-      />
-      <Spacer y={1} />
-      <Container row>
-        <Button
-          disabled={isDisabled() ?? isLoading}
-          onClick={applySettings}
-          status={getStatus()}
-        >
-          Save settings
-        </Button>
-        <Spacer inline x={1} />
-        <ToggleRow
-          isToggled={soc2Enabled}
-          onToggle={() => {
-            setSoc2Enabled((prev) => !prev);
-          }}
-          disabled={isReadOnly}
-          disabledTooltip={
-            "Wait for provisioning to complete before editing this field."
-          }
-        >
-          <Container row>
-            <Text>Enable All</Text>
-          </Container>
-        </ToggleRow>
-      </Container>
-    </StyledCompliance>
-  );
-};
-
-export default Compliance;
-
-const StyledCompliance = styled.div``;
-
-const NewBadge = styled.div`
-  font-size: 13px;
-  padding: 5px 10px;
-  background: linear-gradient(110deg, #b6d5f2, #6836e2);
-  border-radius: 5px;
-  display: flex;
-  align-items: center;
-
-  > img {
-    height: 14px;
-    margin-right: 5px;
-  }
-`;

+ 0 - 135
dashboard/src/main/home/cluster-dashboard/dashboard/DonutChart.tsx

@@ -1,135 +0,0 @@
-import React, { useEffect, useState } from "react";
-import { ArcElement, CategoryScale, Chart, Legend, Tooltip } from "chart.js";
-import { Doughnut } from "react-chartjs-2";
-
-import Container from "components/porter/Container";
-import Spacer from "components/porter/Spacer";
-import { type Soc2Check } from "shared/types";
-
-Chart.register(ArcElement, Tooltip, Legend, CategoryScale);
-
-type DonutChartProps = {
-    data: Soc2Check;
-};
-
-const DonutChart: React.FC<DonutChartProps> = ({ data }) => {
-    const [chartDataValues, setChartDataValues] = useState([0, 0, 0]);
-
-    useEffect(() => {
-        const counts = { ENABLED: 0, DISABLED: 0, PENDING: 0 };
-
-        Object.values(data.soc2_checks).forEach((check) => {
-            let status = check.status || "DISABLED";
-            if (status.includes("PENDING")) {
-                status = "PENDING";
-            }
-            counts[status.toUpperCase()]++;
-        });
-
-        setChartDataValues([counts.ENABLED, counts.DISABLED, counts.PENDING]);
-    }, [data]); // Dependency array ensures this runs only when `data` changes
-
-    const chartData = {
-        labels: ["Enabled", "Disabled", "Pending"],
-        datasets: [
-            {
-                data: chartDataValues,
-                backgroundColor: ["#5eaa7d", "#e34040", "rgb(255, 205, 86)"],
-                borderColor: "#171b21",
-                borderWidth: 2,
-                hoverBorderColor: "#171b21",
-                hoverBorderWidth: 3,
-                borderJoinStyle: "round",
-                hoverBorderJoinStyle: "bevel",
-            },
-        ],
-    };
-
-    const options = {
-        plugins: {
-            legend: false,
-            tooltip: {},
-        },
-        elements: {
-            arc: {
-                borderWidth: 3,
-                borderColor: "#fff",
-                borderAlign: "inner",
-                hoverOffset: 1,
-            },
-        },
-        responsive: true,
-        maintainAspectRatio: false,
-    };
-
-    const textCenter = {
-        id: "textCenter",
-        afterDatasetsDraw: (chart: unknown) => {
-            const { ctx, data } = chart;
-            ctx.save();
-            ctx.font = "15px sans-serif";
-            ctx.fillStyle = "#fff";
-            ctx.textAlign = "center";
-            ctx.textBaseline = "middle";
-
-            // Calculate the total
-            const total = data.datasets[0].data.reduce((a, b) => a + b, 0);
-
-            // Coordinates for the text
-            const x = chart.getDatasetMeta(0).data[0].x;
-            const y = chart.getDatasetMeta(0).data[0].y;
-
-            // Draw the first line of text
-            ctx.fillText(`${data.datasets[0].data[0]} / ${total}`, x, y - 10); // Adjust Y position as needed
-
-            // Draw the second line of text
-            ctx.fillText(`checks enabled`, x, y + 10); // Adjust Y position as needed
-
-            ctx.restore();
-        },
-    };
-
-    const CustomLegend = (): JSX.Element => (
-        <div
-            style={{
-                display: "flex",
-                flexDirection: "column",
-                justifyContent: "center",
-                alignItems: "start",
-            }}
-        >
-            {chartData.datasets[0].backgroundColor.map((color, index) => (
-                <div
-                    key={index}
-                    style={{ display: "flex", alignItems: "center", marginBottom: "4px" }}
-                >
-                    <span
-                        style={{
-                            backgroundColor: color,
-                            width: "12px",
-                            height: "12px",
-                            display: "inline-block",
-                            marginRight: "8px",
-                        }}
-                    ></span>
-                    {chartData.labels[index]}
-                </div>
-            ))}
-        </div>
-    );
-
-    return (
-        <>
-            <Spacer y={0.5} />
-            <Container row>
-                <div style={{ width: "300px", height: "300px" }}>
-                    <Doughnut data={chartData} options={options} plugins={[textCenter]} />
-                </div>
-                <Spacer inline x={1} />
-                <CustomLegend />
-            </Container>
-        </>
-    );
-};
-
-export default DonutChart;

+ 0 - 246
dashboard/src/main/home/cluster-dashboard/dashboard/PorterAppDashboard.tsx

@@ -1,246 +0,0 @@
-import React, { useState, useContext, useEffect } from "react";
-import styled from "styled-components";
-import { RouteComponentProps, withRouter } from "react-router";
-
-import gradient from "assets/gradient.png";
-
-import { Context } from "shared/Context";
-import { InfraType } from "shared/types";
-import api from "shared/api";
-import { pushFiltered, pushQueryParams } from "shared/routing";
-import { withAuth, WithAuthProps } from "shared/auth/AuthorizationHoc";
-
-import ProvisionerSettings from "../provisioner/ProvisionerSettings";
-import ClusterPlaceholderContainer from "../ClusterPlaceholderContainer";
-import TabRegion from "components/TabRegion";
-import FormDebugger from "components/porter-form/FormDebugger";
-import TitleSection from "components/TitleSection";
-import ClusterSection from "../dashboard/ClusterSection";
-import { StatusPage } from "../onboarding/steps/ProvisionResources/forms/StatusPage";
-import Banner from "components/porter/Banner";
-import Spacer from "components/porter/Spacer";
-
-type Props = RouteComponentProps & WithAuthProps & {
-  projectId: number | null;
-  setRefreshClusters: (x: boolean) => void;
-};
-
-const PorterAppDashboard: React.FC<Props> = ({
-  projectId,
-  setRefreshClusters,
-  ...props
-}) => {
-  const { currentProject, user, capabilities, usage } = useContext(Context);
-  const [infras, setInfras] = useState<InfraType[]>([]);
-  const [pressingCtrl, setPressingCtrl] = useState(false);
-  const [pressingK, setPressingK] = useState(false);
-  const [showFormDebugger, setShowFormDebugger] = useState(false);
-  const [tabOptions, setTabOptions] = useState([{
-    label: "Connected clusters",
-    value: "overview"
-  }]);
-
-  const handleKeyDown = (e: KeyboardEvent): void => {
-    if (e.key === "k") {
-      setPressingK(true);
-    }
-    if (e.key === "Meta" || e.key === "Control") {
-      setPressingCtrl(true);
-    }
-    if (e.key === "z" && pressingK && pressingCtrl) {
-      setPressingK(false);
-      setPressingCtrl(false);
-      setShowFormDebugger(!showFormDebugger);
-    }
-  };
-
-  const handleKeyUp = (e: KeyboardEvent): void => {
-    if (e.key === "Meta" || e.key === "Control" || e.key === "k") {
-      setPressingK(false);
-      setPressingCtrl(false);
-    }
-  };
-
-  useEffect(() => {
-    document.addEventListener("keydown", handleKeyDown);
-    document.addEventListener("keyup", handleKeyUp);
-    return () => {
-      document.removeEventListener("keydown", handleKeyDown);
-      document.removeEventListener("keyup", handleKeyUp);
-    };
-  }, []);
-
-  useEffect(() => {
-    if (currentProject) {
-      if (currentProject.simplified_view_enabled) {
-        pushFiltered(props, "/apps", ["project_id"]);
-      }
-      api
-        .getInfra(
-          "<token>",
-          {},
-          {
-            project_id: currentProject.id,
-          }
-        )
-        .then((res) => setInfras(res.data))
-        .catch(console.log);
-    }
-  }, [currentProject]);
-
-  const currentTab = () => new URLSearchParams(props.location.search).get("tab") || "overview";
-
-  useEffect(() => {
-    if (usage && usage?.current?.clusters < usage?.limit?.clusters) {
-      tabOptions.push({ label: "Create a cluster", value: "create-cluster" });
-    }
-
-    tabOptions.push({ label: "Provisioner status", value: "provisioner" });
-
-    if (!capabilities?.provisioner) {
-      let newTabs = [{ label: "Project overview", value: "overview" }];
-      setTabOptions(newTabs);
-    } else {
-      setTabOptions(tabOptions);
-    }
-  }, [currentProject]);
-
-  const renderTabContents = () => {
-
-    return <ClusterPlaceholderContainer />;
-  };
-
-  return (
-    <>
-      {currentProject && (
-        <DashboardWrapper>
-          {showFormDebugger ? (
-            <FormDebugger
-              goBack={() => setShowFormDebugger(false)}
-            />
-          ) : (
-            <>
-              <TitleSection>
-                {/* <DashboardIcon>
-                  <DashboardImage src={gradient} />
-                  <Overlay>
-                    {currentProject && currentProject.name[0].toUpperCase()}
-                  </Overlay>
-                </DashboardIcon>
-                {currentProject && currentProject.name}
-                {currentProject?.roles?.filter((obj: any) => {
-                  return obj.user_id === user.userId;
-                })[0].kind === "admin" || (
-                    <i
-                      className="material-icons"
-                      onClick={() => {
-                        pushFiltered(props, "/project-settings", ["project_id"]);
-                      }}
-                    >
-                      more_vert
-                    </i>
-                  )} */}
-                Select Cluster
-              </TitleSection>
-              <Spacer height="15px" />
-              {
-                <ClusterPlaceholderContainer />
-                // <TabRegion
-                //   currentTab={currentTab()}
-                //   setCurrentTab={(x: string) => {
-                //     pushQueryParams(props, { tab: x });
-                //   }}
-                //   options={tabOptions}
-                // >
-                //   {renderTabContents()}
-                // </TabRegion>
-
-              }
-            </>
-          )}
-        </DashboardWrapper>
-      )}
-    </>
-  );
-};
-
-export default withRouter(withAuth(PorterAppDashboard));
-
-const Br = styled.div`
-  width: 100%;
-  height: 1px;
-`;
-
-const DashboardWrapper = styled.div`
-  padding-bottom: 100px;
-`;
-
-const TopRow = styled.div`
-  display: flex;
-  align-items: center;
-`;
-
-const Description = styled.div`
-  color: #aaaabb;
-  margin-top: 13px;
-  margin-left: 2px;
-  font-size: 13px;
-`;
-
-const InfoLabel = styled.div`
-  width: 72px;
-  height: 20px;
-  display: flex;
-  align-items: center;
-  color: #aaaabb;
-  font-size: 13px;
-  > i {
-    color: #aaaabb;
-    font-size: 18px;
-    margin-right: 5px;
-  }
-`;
-
-const InfoSection = styled.div`
-  margin-top: 20px;
-  font-family: "Work Sans", sans-serif;
-  margin-left: 0px;
-  margin-bottom: 30px;
-`;
-
-const Overlay = styled.div`
-  height: 100%;
-  width: 100%;
-  position: absolute;
-  top: 0;
-  left: 0;
-  border-radius: 5px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  font-size: 21px;
-  font-weight: 500;
-  font-family: "Work Sans", sans-serif;
-  color: white;
-`;
-
-const DashboardImage = styled.img`
-  height: 35px;
-  width: 35px;
-  border-radius: 5px;
-`;
-
-const DashboardIcon = styled.div`
-  position: relative;
-  height: 35px;
-  margin-right: 17px;
-  width: 35px;
-  border-radius: 5px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-
-  > i {
-    font-size: 22px;
-  }
-`;

+ 0 - 104
dashboard/src/main/home/cluster-dashboard/expanded-chart/DeploymentTypeStacks.tsx

@@ -1,104 +0,0 @@
-import React, { useState } from "react";
-import styled from "styled-components";
-
-import { integrationList } from "shared/common";
-import { ChartType } from "shared/types";
-
-type Props = {
-  appData: any;
-};
-
-const DeploymentTypeStacks: React.FC<Props> = ({ appData }) => {
-  const [showRepoTooltip, setShowRepoTooltip] = useState(false);
-
-  const githubRepository = appData?.app.repo_name;
-  const icon = githubRepository
-    ? integrationList.repo.icon
-    : integrationList.registry.icon;
-
-  const repository =
-    githubRepository ||
-    appData.cluster?.image_repo_uri ||
-    appData.cluster?.config?.image?.repository;
-
-  if (repository?.includes("hello-porter")) {
-    return null;
-  }
-
-  return (
-    <DeploymentImageContainer>
-      <DeploymentTypeIcon src={icon} />
-      <RepositoryName
-        onMouseOver={() => {
-          setShowRepoTooltip(true);
-        }}
-        onMouseOut={() => {
-          setShowRepoTooltip(false);
-        }}
-      >
-        {repository}
-      </RepositoryName>
-      {showRepoTooltip && <Tooltip>{repository}</Tooltip>}
-    </DeploymentImageContainer>
-  );
-};
-
-export default DeploymentTypeStacks;
-
-const DeploymentImageContainer = styled.div`
-  height: 20px;
-  font-size: 13px;
-  position: relative;
-  display: flex;
-  margin-left: 15px;
-  margin-bottom: -3px;
-  align-items: center;
-  font-weight: 400;
-  justify-content: center;
-  color: #ffffff66;
-  padding-left: 5px;
-`;
-
-const Icon = styled.img`
-  width: 100%;
-`;
-
-const DeploymentTypeIcon = styled(Icon)`
-  width: 20px;
-  margin-right: 10px;
-`;
-
-const RepositoryName = styled.div`
-  white-space: nowrap;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  max-width: 390px;
-  position: relative;
-  margin-right: 3px;
-`;
-
-const Tooltip = styled.div`
-  position: absolute;
-  left: -40px;
-  top: 28px;
-  min-height: 18px;
-  max-width: calc(700px);
-  padding: 5px 7px;
-  background: #272731;
-  z-index: 999;
-  color: white;
-  font-size: 12px;
-  font-family: "Work Sans", sans-serif;
-  outline: 1px solid #ffffff55;
-  opacity: 0;
-  animation: faded-in 0.2s 0.15s;
-  animation-fill-mode: forwards;
-  @keyframes faded-in {
-    from {
-      opacity: 0;
-    }
-    to {
-      opacity: 1;
-    }
-  }
-`;

+ 0 - 115
dashboard/src/main/home/cluster-dashboard/expanded-chart/deploy/DeploySection.tsx

@@ -1,115 +0,0 @@
-import React, { Component } from "react";
-import styled from "styled-components";
-
-import Loading from "components/Loading";
-import { Context } from "shared/Context";
-import { ChartType } from "shared/types";
-
-import EventTab from "./EventTab";
-
-type PropsType = {
-  currentChart: ChartType;
-};
-
-type StateType = {
-  events: any[];
-  loading: boolean;
-};
-
-export default class StatusSection extends Component<PropsType, StateType> {
-  state = {
-    events: [] as any[],
-    loading: true,
-  };
-
-  renderTabs = () => {
-    return this.state.events.map((c, i) => {
-      return <EventTab />;
-    });
-  };
-
-  renderStatusSection = () => {
-    if (this.state.loading) {
-      return (
-        <NoEvents>
-          <Loading />
-        </NoEvents>
-      );
-    }
-    if (this.state.events.length > 0) {
-      return <Wrapper>{this.renderTabs()}</Wrapper>;
-    } else {
-      return (
-        <NoEvents>
-          <i className="material-icons">category</i>
-          No events to display. This might happen while your app is still
-          deploying.
-        </NoEvents>
-      );
-    }
-  };
-
-  componentDidMount() {
-    const { currentChart } = this.props;
-    let { currentCluster, currentProject, setCurrentError } = this.context;
-
-    // api.getChartEvents('<token>', {
-    //   namespace: currentChart.namespace,
-    //   cluster_id: currentCluster.id,
-    //   storage: StorageType.Secret
-    // }, {
-    //   id: currentProject.id,
-    //   name: currentChart.name,
-    //   revision: currentChart.version
-    // }, (err: any, res: any) => {
-    //   if (err) {
-    //     setCurrentError(JSON.stringify(err));
-    //     return
-    //   }
-    //   this.setState({ controllers: res.data, loading: false })
-    // });
-    this.setState({ events: [1, 2, 3], loading: false });
-  }
-
-  render() {
-    return (
-      <StyledDeploySection>{this.renderStatusSection()}</StyledDeploySection>
-    );
-  }
-}
-
-StatusSection.contextType = Context;
-
-const StyledDeploySection = styled.div`
-  width: 100%;
-  height: 100%;
-  position: relative;
-  font-size: 13px;
-  padding: 0px;
-  user-select: text;
-  border-radius: 5px;
-  overflow: hidden;
-`;
-
-const Wrapper = styled.div`
-  width: 100%;
-  height: 100%;
-  overflow-y: auto;
-  min-width: 250px;
-`;
-
-const NoEvents = styled.div`
-  padding-top: 20%;
-  position: relative;
-  width: 100%;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  color: #ffffff44;
-  font-size: 14px;
-
-  > i {
-    font-size: 18px;
-    margin-right: 12px;
-  }
-`;

+ 0 - 42
dashboard/src/main/home/cluster-dashboard/expanded-chart/deploy/EventTab.tsx

@@ -1,42 +0,0 @@
-import React, { Component } from "react";
-import styled from "styled-components";
-
-type PropsType = {};
-
-type StateType = {};
-
-export default class EventTab extends Component<PropsType, StateType> {
-  state = {};
-
-  render() {
-    return (
-      <StyledEventTab isLast={false}>
-        <EventHeader>
-          <i className="material-icons">cloud_upload</i>
-          Deploy successful!
-          <div>Dec 12 at 11:55AM</div>
-        </EventHeader>
-      </StyledEventTab>
-    );
-  }
-}
-
-const StyledEventTab = styled.div`
-  width: 100%;
-  margin-bottom: 2px;
-  background: #ffffff11;
-  border-bottom-left-radius: ${(props: { isLast: boolean }) =>
-    props.isLast ? "5px" : ""};
-`;
-
-const EventHeader = styled.div`
-  width: 100%;
-  height: 50px;
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  color: #ffffff66;
-  user-select: none;
-  padding: 8px 18px;
-  padding-left: 22px;
-`;

+ 0 - 135
dashboard/src/main/home/cluster-dashboard/expanded-chart/jobs/TempJobList.tsx

@@ -1,135 +0,0 @@
-import React, { useContext, useState } from "react";
-import styled from "styled-components";
-
-import { PorterFormContext } from "components/porter-form/PorterFormContextProvider";
-import JobList from "./JobList";
-import SaveButton from "components/SaveButton";
-import CommandLineIcon from "assets/command-line-icon";
-import ConnectToJobInstructionsModal from "./ConnectToJobInstructionsModal";
-import Loading from "components/Loading";
-
-interface Props {
-  isAuthorized: any;
-  saveValuesStatus: string;
-  setJobs: any;
-  jobs: any;
-  handleSaveValues: any;
-  expandJob: any;
-  currentChartVersion: number;
-  latestChartVersion: number;
-  isDeployedFromGithub: boolean;
-  repositoryUrl?: string;
-  chartName: string;
-  isLoading: boolean;
-}
-
-/**
- * Temporary functional component for allowing job rerun button to consume
- * form context (until ExpandedJobChart is migrated to FC)
- */
-const TempJobList: React.FC<Props> = (props) => {
-  const { getSubmitValues } = useContext(PorterFormContext);
-  const [showConnectionModal, setShowConnectionModal] = useState(false);
-  const [searchInput, setSearchInput] = useState("");
-
-  let saveButton = (
-    <ButtonWrapper>
-      <SaveButton
-        onClick={() => {
-          props.handleSaveValues(getSubmitValues(), true);
-        }}
-        status={props.saveValuesStatus}
-        makeFlush={true}
-        clearPosition={true}
-        rounded={true}
-        statusPosition="right"
-      >
-        <i className="material-icons">play_arrow</i> Run Job
-      </SaveButton>
-      <CLIModalIconWrapper
-        onClick={(e) => {
-          e.preventDefault();
-          setShowConnectionModal(true);
-        }}
-      >
-        <CLIModalIcon />
-        Shell Access
-      </CLIModalIconWrapper>
-    </ButtonWrapper>
-  );
-
-  if (!props.isAuthorized("job", "", ["get", "update", "create"])) {
-    saveButton = null;
-  }
-
-  if (props.isLoading) {
-    return <Loading height="500px"></Loading>;
-  }
-
-  return (
-    <>
-      {saveButton}
-      <JobList
-        jobs={props.jobs}
-        setJobs={props.setJobs}
-        expandJob={props.expandJob}
-        isDeployedFromGithub={props.isDeployedFromGithub}
-        repositoryUrl={props.repositoryUrl}
-        currentChartVersion={props.currentChartVersion}
-        latestChartVersion={props.latestChartVersion}
-      />
-      <ConnectToJobInstructionsModal
-        show={showConnectionModal}
-        onClose={() => setShowConnectionModal(false)}
-        chartName={props.chartName}
-      />
-    </>
-  );
-};
-
-export default TempJobList;
-
-const ButtonWrapper = styled.div`
-  display: flex;
-  margin: 5px 0 35px;
-  justify-content: space-between;
-`;
-
-const CLIModalIconWrapper = styled.div`
-  height: 35px;
-  font-size: 13px;
-  font-weight: 500;
-  font-family: "Work Sans", sans-serif;
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  padding: 6px 20px 6px 10px;
-  text-align: left;
-  border: 1px solid #ffffff55;
-  border-radius: 8px;
-  background: #ffffff11;
-  color: #ffffffdd;
-  cursor: pointer;
-
-  :hover {
-    cursor: pointer;
-    background: #ffffff22;
-    > path {
-      fill: #ffffff77;
-    }
-  }
-
-  > path {
-    fill: #ffffff99;
-  }
-`;
-
-const CLIModalIcon = styled(CommandLineIcon)`
-  width: 32px;
-  height: 32px;
-  padding: 8px;
-
-  > path {
-    fill: #ffffff99;
-  }
-`;

+ 0 - 52
dashboard/src/main/home/cluster-dashboard/expanded-chart/status/ConnectToLogsInstructionModal.tsx

@@ -1,52 +0,0 @@
-import Modal from "main/home/modals/Modal";
-import React from "react";
-import styled from "styled-components";
-
-const ConnectToLogsInstructionModal: React.FC<{
-  show: boolean;
-  onClose: () => void;
-  chartName: string;
-  namespace: string;
-}> = ({ show, chartName, namespace, onClose }) => {
-  if (!show) {
-    return null;
-  }
-
-  return (
-    <Modal
-      onRequestClose={() => onClose()}
-      width="700px"
-      height="300px"
-      title="Shell Access Instructions"
-    >
-      To get shell live logs for this pod, make sure you have the Porter CLI
-      installed (installation instructions&nbsp;
-      <a href={"https://docs.porter.run/cli/installation"} target="_blank">
-        here
-      </a>
-      ).
-      <br />
-      <br />
-      Run the following line of code:
-      <Code>
-        porter logs {chartName || "[APP-NAME]"} --follow --namespace{" "}
-        {namespace || "[NAMESPACE]"}
-      </Code>
-    </Modal>
-  );
-};
-
-export default ConnectToLogsInstructionModal;
-
-const Code = styled.div`
-  background: #181b21;
-  padding: 10px 15px;
-  border: 1px solid #ffffff44;
-  border-radius: 5px;
-  margin: 10px 0px 15px;
-  color: #ffffff;
-  font-size: 13px;
-  user-select: text;
-  line-height: 1em;
-  font-family: monospace;
-`;

+ 0 - 123
dashboard/src/main/home/cluster-dashboard/preview-environments/components/RecreateWorkflowFilesModal.tsx

@@ -1,123 +0,0 @@
-import Modal from "main/home/modals/Modal";
-import React from "react";
-import styled from "styled-components";
-
-type Props = {
-  hide: boolean;
-  isReEnable: boolean;
-  onClose: () => void;
-};
-
-const RecreateWorkflowFilesModal = (props: Props) => {
-  const createNewWorkflows = () => {};
-
-  if (props.hide) {
-    return null;
-  }
-
-  return (
-    <Modal title="Workflow files not found">
-      <div>
-        <div>
-          We couldn't find any workflow files to process the{" "}
-          {props.isReEnable
-            ? "re enabling of this preview environment"
-            : "creation of this preview environment"}
-          .
-          <HighlightText>
-            Do you want to create the workflow files? Or Remove the repository?
-          </HighlightText>
-          <Warning highlight>
-            ⚠️ If the workflow files don't exist, Porter will not be able to
-            create any preview environment for this repository.
-          </Warning>
-        </div>
-
-        <ActionWrapper>
-          <DeleteButton onClick={() => props.onClose()}>Close</DeleteButton>
-          <CancelButton onClick={() => createNewWorkflows()}>
-            Create new workflows
-          </CancelButton>
-        </ActionWrapper>
-      </div>
-    </Modal>
-  );
-};
-
-export default RecreateWorkflowFilesModal;
-
-const Button = styled.button`
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-  justify-content: space-between;
-  font-size: 13px;
-  cursor: pointer;
-  font-family: "Work Sans", sans-serif;
-  border-radius: 10px;
-  color: white;
-  height: 35px;
-  padding: 10px 16px;
-  font-weight: 500;
-  overflow: hidden;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-  cursor: pointer;
-  border: none;
-  :not(:last-child) {
-    margin-right: 10px;
-  }
-`;
-
-const DeleteButton = styled(Button)`
-  ${({ disabled }: { disabled?: boolean }) => {
-    if (disabled) {
-      return `
-      background: #aaaabbee;
-      :hover {
-        background: #aaaabbee;
-      }    
-      `;
-    }
-
-    return `
-      background: #dd4b4b;
-      :hover {
-        background: #b13d3d;
-      }`;
-  }}
-`;
-
-const CancelButton = styled(Button)`
-  background: #616feecc;
-  :hover {
-    background: #505edddd;
-  }
-`;
-
-const ActionWrapper = styled.div`
-  display: flex;
-  align-items: center;
-`;
-
-const Warning = styled.div`
-  font-size: 13px;
-  display: flex;
-  width: 100%;
-  margin-top: 10px;
-  line-height: 1.4em;
-  align-items: center;
-  color: white;
-  > i {
-    margin-right: 10px;
-    font-size: 18px;
-  }
-  color: ${(props: { highlight: boolean }) =>
-    props.highlight ? "#f5cb42" : ""};
-`;
-
-const HighlightText = styled.div`
-  font-size: 16px;
-  font-weight: bold;
-  color: #ffffff;
-`;

+ 0 - 243
dashboard/src/main/home/cluster-dashboard/preview-environments/deployments/PullRequestCard.tsx

@@ -1,243 +0,0 @@
-import React, { useState, useContext } from "react";
-import styled from "styled-components";
-import pr_icon from "assets/pull_request_icon.svg";
-import { PullRequest } from "../types";
-import api from "shared/api";
-import { Context } from "shared/Context";
-import { ActionButton } from "../components/ActionButton";
-import Loading from "components/Loading";
-import RecreateWorkflowFilesModal from "../components/RecreateWorkflowFilesModal";
-import { EllipsisTextWrapper, RepoLink } from "../components/styled";
-
-const PullRequestCard = ({
-  pullRequest,
-  onCreation,
-}: {
-  pullRequest: PullRequest;
-  onCreation: (pullRequest: PullRequest) => void;
-}) => {
-  const { currentProject, currentCluster, setCurrentError } = useContext(
-    Context
-  );
-  const [showMergeInfoTooltip, setShowMergeInfoTooltip] = useState(false);
-  const [
-    openRecreateWorkflowFilesModal,
-    setOpenRecreateWorkflowFilesModal,
-  ] = useState(false);
-  const [isLoading, setIsLoading] = useState(false);
-  const [hasError, setHasError] = useState(false);
-
-  const createPreviewEnvironment = async () => {
-    setIsLoading(true);
-    try {
-      await api.createPreviewEnvironmentDeployment("<token>", pullRequest, {
-        cluster_id: currentCluster?.id,
-        project_id: currentProject?.id,
-      });
-      onCreation(pullRequest);
-    } catch (error) {
-      setCurrentError(error?.response?.data?.error || error);
-      setHasError(true);
-      setTimeout(() => {
-        setHasError(false);
-      }, 500);
-    } finally {
-      setIsLoading(false);
-    }
-  };
-
-  return (
-    <>
-      <RecreateWorkflowFilesModal
-        hide={!openRecreateWorkflowFilesModal}
-        onClose={() => setOpenRecreateWorkflowFilesModal(false)}
-        isReEnable={false}
-      />
-      <DeploymentCardWrapper>
-        <DataContainer>
-          <PRName>
-            <PRIcon src={pr_icon} alt="pull request icon" />
-            <EllipsisTextWrapper tooltipText={pullRequest.pr_title}>
-              {pullRequest.pr_title}
-            </EllipsisTextWrapper>
-            <InfoWrapper>
-              <MergeInfo
-                onMouseOver={() => setShowMergeInfoTooltip(true)}
-                onMouseOut={() => setShowMergeInfoTooltip(false)}
-              >
-                {pullRequest.branch_from}
-                <i className="material-icons">arrow_forward</i>
-                {pullRequest.branch_into}
-              </MergeInfo>
-              {showMergeInfoTooltip && (
-                <Tooltip>
-                  From: {pullRequest.branch_from} Into:{" "}
-                  {pullRequest.branch_into}
-                </Tooltip>
-              )}
-            </InfoWrapper>
-            <RepoLink
-              to={`https://github.com/${pullRequest.repo_owner}/${pullRequest.repo_name}/pull/${pullRequest.pr_number}`}
-              target="_blank"
-            >
-              <i className="material-icons">open_in_new</i>
-              View PR
-            </RepoLink>
-          </PRName>
-
-          <Flex>
-            <StatusContainer>
-              <Status>
-                <StatusDot />
-                Not deployed
-              </Status>
-            </StatusContainer>
-          </Flex>
-        </DataContainer>
-        <Flex>
-          <ActionButton
-            onClick={createPreviewEnvironment}
-            disabled={isLoading}
-            hasError={hasError}
-          >
-            {isLoading ? (
-              <Loading width="198px" height="14px" />
-            ) : (
-              <>
-                <i className="material-icons">play_arrow</i>
-                Activate Preview Environment
-              </>
-            )}
-          </ActionButton>
-        </Flex>
-      </DeploymentCardWrapper>
-    </>
-  );
-};
-
-export default PullRequestCard;
-
-const Flex = styled.div`
-  display: flex;
-  align-items: center;
-`;
-
-const PRName = styled.div`
-  font-family: "Work Sans", sans-serif;
-  font-weight: 500;
-  color: #ffffff;
-  display: flex;
-  align-items: center;
-  margin-bottom: 10px;
-`;
-
-const DeploymentCardWrapper = styled.div`
-  display: flex;
-  justify-content: space-between;
-  font-size: 13px;
-  height: 75px;
-  padding: 12px;
-  padding-left: 14px;
-  border-radius: 5px;
-  background: #26292e;
-  border: 1px solid #494b4f;
-
-  animation: fadeIn 0.5s;
-  @keyframes fadeIn {
-    from {
-      opacity: 0;
-    }
-    to {
-      opacity: 1;
-    }
-  }
-`;
-
-const DataContainer = styled.div`
-  display: flex;
-  flex-direction: column;
-  justify-content: space-between;
-`;
-
-const StatusContainer = styled.div`
-  display: flex;
-  flex-direction: column;
-  justify-content: flex-start;
-  height: 100%;
-`;
-
-const PRIcon = styled.img`
-  font-size: 20px;
-  height: 17px;
-  margin-right: 10px;
-  color: #aaaabb;
-  opacity: 50%;
-`;
-
-const Status = styled.span`
-  font-size: 13px;
-  display: flex;
-  align-items: center;
-  min-height: 17px;
-  color: #a7a6bb;
-`;
-
-const StatusDot = styled.div`
-  width: 8px;
-  height: 8px;
-  margin-right: 10px;
-  background: #ffffff88;
-  border-radius: 20px;
-  margin-left: 3px;
-`;
-
-const Tooltip = styled.div`
-  position: absolute;
-  left: 14px;
-  top: 20px;
-  min-height: 18px;
-  max-width: calc(700px);
-  padding: 5px 7px;
-  background: #272731;
-  z-index: 999;
-  color: white;
-  font-size: 12px;
-  font-family: "Work Sans", sans-serif;
-  outline: 1px solid #ffffff55;
-  opacity: 0;
-  animation: faded-in 0.2s 0.15s;
-  animation-fill-mode: forwards;
-  @keyframes faded-in {
-    from {
-      opacity: 0;
-    }
-    to {
-      opacity: 1;
-    }
-  }
-`;
-
-const InfoWrapper = styled.div`
-  display: flex;
-  align-items: center;
-  margin-right: 8px;
-  position: relative;
-`;
-
-const MergeInfo = styled.div`
-  font-size: 13px;
-  margin-left: 14px;
-  align-items: center;
-  color: #aaaabb66;
-  white-space: nowrap;
-  display: flex;
-  align-items: center;
-  text-overflow: ellipsis;
-  overflow: hidden;
-  max-width: 300px;
-
-  > i {
-    font-size: 16px;
-    margin: 0 2px;
-  }
-`;

+ 0 - 264
dashboard/src/main/home/dashboard/ClusterOverview.tsx

@@ -1,264 +0,0 @@
-import React, { Component } from "react";
-import styled from "styled-components";
-
-import { Context } from "shared/Context";
-import api from "shared/api";
-import { ClusterType, DetailedClusterType } from "shared/types";
-import Helper from "components/form-components/Helper";
-import { pushFiltered } from "shared/routing";
-
-import { RouteComponentProps, withRouter } from "react-router";
-
-import Modal from "../modals/Modal";
-import Heading from "components/form-components/Heading";
-
-type PropsType = RouteComponentProps & {
-  currentCluster: ClusterType;
-};
-
-type StateType = {
-  loading: boolean;
-  error: string;
-  clusters: DetailedClusterType[];
-  showErrorModal?: {
-    clusterId: number;
-    show: boolean;
-  };
-};
-
-class Templates extends Component<PropsType, StateType> {
-  state: StateType = {
-    loading: true,
-    error: "",
-    clusters: [],
-    showErrorModal: undefined,
-  };
-
-  componentDidMount() {
-    this.updateClusterList();
-  }
-
-  componentDidUpdate(prevProps: PropsType) {
-    if (prevProps.currentCluster?.name != this.props.currentCluster?.name) {
-      this.updateClusterList();
-    }
-  }
-
-  updateClusterList = async () => {
-    try {
-      const res = await api.getClusters(
-        "<token>",
-        {},
-        { id: this.context.currentProject.id }
-      );
-
-      if (res.data) {
-        this.setState({ clusters: res.data, loading: false, error: "" });
-      } else {
-        this.setState({ loading: false, error: "Response data missing" });
-      }
-    } catch (err) {
-      this.setState(err);
-    }
-  };
-
-  renderIcon = () => {
-    return (
-      <DashboardIcon>
-        <svg
-          width="16"
-          height="16"
-          viewBox="0 0 19 19"
-          fill="none"
-          xmlns="http://www.w3.org/2000/svg"
-        >
-          <path
-            d="M15.207 12.4403C16.8094 12.4403 18.1092 11.1414 18.1092 9.53907C18.1092 7.93673 16.8094 6.63782 15.207 6.63782"
-            stroke="white"
-            strokeWidth="1.5"
-            strokeLinecap="round"
-            strokeLinejoin="round"
-          />
-          <path
-            d="M3.90217 12.4403C2.29983 12.4403 1 11.1414 1 9.53907C1 7.93673 2.29983 6.63782 3.90217 6.63782"
-            stroke="white"
-            strokeWidth="1.5"
-            strokeLinecap="round"
-            strokeLinejoin="round"
-          />
-          <path
-            fillRule="evenodd"
-            clipRule="evenodd"
-            d="M9.54993 13.4133C7.4086 13.4133 5.69168 11.6964 5.69168 9.55417C5.69168 7.41284 7.4086 5.69592 9.54993 5.69592C11.6913 5.69592 13.4082 7.41284 13.4082 9.55417C13.4082 11.6964 11.6913 13.4133 9.54993 13.4133Z"
-            stroke="white"
-            strokeWidth="1.5"
-            strokeLinecap="round"
-            strokeLinejoin="round"
-          />
-          <path
-            d="M6.66895 15.207C6.66895 16.8094 7.96787 18.1092 9.5702 18.1092C11.1725 18.1092 12.4715 16.8094 12.4715 15.207"
-            stroke="white"
-            strokeWidth="1.5"
-            strokeLinecap="round"
-            strokeLinejoin="round"
-          />
-          <path
-            d="M6.66895 3.90217C6.66895 2.29983 7.96787 1 9.5702 1C11.1725 1 12.4715 2.29983 12.4715 3.90217"
-            stroke="white"
-            strokeWidth="1.5"
-            strokeLinecap="round"
-            strokeLinejoin="round"
-          />
-          <path
-            fillRule="evenodd"
-            clipRule="evenodd"
-            d="M5.69591 9.54996C5.69591 7.40863 7.41283 5.69171 9.55508 5.69171C11.6964 5.69171 13.4133 7.40863 13.4133 9.54996C13.4133 11.6913 11.6964 13.4082 9.55508 13.4082C7.41283 13.4082 5.69591 11.6913 5.69591 9.54996Z"
-            stroke="white"
-            strokeWidth="1.5"
-            strokeLinecap="round"
-            strokeLinejoin="round"
-          />
-        </svg>
-      </DashboardIcon>
-    );
-  };
-
-  renderClusters = () => {
-    return this.state.clusters.map(
-      (cluster: DetailedClusterType, i: number) => {
-        return (
-          <TemplateBlock
-            onClick={() => {
-              this.context.setCurrentCluster(cluster);
-              pushFiltered(this.props, "/applications", ["project_id"], {
-                cluster: cluster.name,
-              });
-            }}
-            key={i}
-          >
-            {this.renderIcon()}
-            <TemplateTitle>{cluster.vanity_name || cluster.name}</TemplateTitle>
-          </TemplateBlock>
-        );
-      }
-    );
-  };
-
-  renderErrorModal = () => {
-    const clusterError =
-      this.state.showErrorModal?.show &&
-      this.state.clusters.find(
-        (c) => c.id === this.state.showErrorModal?.clusterId
-      );
-    const ingressError = clusterError?.ingress_error;
-    return (
-      <>
-        {clusterError && (
-          <Modal
-            onRequestClose={() => this.setState({ showErrorModal: undefined })}
-            width="665px"
-            height="min-content"
-          >
-            Porter encountered an error. Full error log:
-            <CodeBlock>{ingressError.error}</CodeBlock>
-          </Modal>
-        )}
-      </>
-    );
-  };
-
-  render() {
-    return (
-      <StyledClusterList>
-        <Heading isAtTop>Connected clusters</Heading>
-        <TemplateList>{this.renderClusters()}</TemplateList>
-        {this.renderErrorModal()}
-      </StyledClusterList>
-    );
-  }
-}
-
-Templates.contextType = Context;
-
-export default withRouter(Templates);
-
-const CodeBlock = styled.span`
-  display: block;
-  background-color: #1b1d26;
-  color: white;
-  border-radius: 5px;
-  font-family: monospace;
-  user-select: text;
-  max-height: 400px;
-  width: 90%;
-  margin-left: 5%;
-  margin-top: 20px;
-  overflow-y: auto;
-  padding: 10px;
-  overflow-wrap: break-word;
-`;
-
-const StyledClusterList = styled.div`
-  padding-left: 2px;
-  overflow: visible;
-`;
-
-const DashboardIcon = styled.div`
-  position: relative;
-  height: 25px;
-  min-width: 25px;
-  width: 25px;
-  border-radius: 200px;
-  margin-right: 15px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  background: #676c7c;
-  border: 1px solid #8e94aa;
-  > i {
-    font-size: 22px;
-  }
-`;
-
-const TemplateTitle = styled.div`
-  text-align: center;
-  white-space: nowrap;
-  overflow: hidden;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-`;
-
-const TemplateBlock = styled.div`
-  align-items: center;
-  user-select: none;
-  display: flex;
-  font-size: 13px;
-  font-weight: 500;
-  padding: 15px;
-  margin-bottom: 20px;
-  align-item: center;
-  cursor: pointer;
-  color: #ffffff;
-  position: relative;
-  border-radius: 5px;
-  background: ${props => props.theme.clickable.bg};
-  border: 1px solid #494b4f;
-  :hover {
-    border: 1px solid #7a7b80;
-  }
-
-  animation: fadeIn 0.3s 0s;
-  @keyframes fadeIn {
-    from {
-      opacity: 0;
-    }
-    to {
-      opacity: 1;
-    }
-  }
-`;
-
-const TemplateList = styled.div`
-  overflow-y: auto;
-  overflow: visible;
-`;

+ 0 - 28
dashboard/src/main/home/dashboard/PipelinesSection.tsx

@@ -1,28 +0,0 @@
-import React, { Component } from "react";
-import styled from "styled-components";
-
-type PropsType = {};
-
-type StateType = {};
-
-export default class PipelinesSection extends Component<PropsType, StateType> {
-  state = {};
-
-  render() {
-    return <StyledPipelinesSection></StyledPipelinesSection>;
-  }
-}
-
-const StyledPipelinesSection = styled.div`
-  width: 100%;
-  height: calc(100vh - 380px);
-  margin-top: 30px;
-  display: flex;
-  align-items: center;
-  color: #aaaabb;
-  border-radius: 5px;
-  text-align: center;
-  font-size: 13px;
-  background: #ffffff08;
-  font-family: "Work Sans", sans-serif;
-`;

+ 0 - 56
dashboard/src/main/home/database-dashboard/DatabaseHeaderItem.tsx

@@ -1,56 +0,0 @@
-import React from "react";
-import styled from "styled-components";
-
-import CopyToClipboard from "components/CopyToClipboard";
-import Container from "components/porter/Container";
-import Text from "components/porter/Text";
-import { type DatastoreMetadataWithSource } from "lib/databases/types";
-
-import copy from "assets/copy-left.svg";
-
-type DatabaseHeaderItemProps = {
-  item: DatastoreMetadataWithSource;
-};
-
-const DatabaseHeaderItem: React.FC<DatabaseHeaderItemProps> = ({ item }) => {
-  const truncateText = (text: string, length: number): string => {
-    if (text.length <= length) {
-      return text;
-    }
-    return `${text.substring(0, length)}...`;
-  };
-
-  const titleizeText = (text: string): string => {
-    return text
-      .split("_")
-      .map((word) => {
-        return word[0].toUpperCase() + word.substring(1);
-      })
-      .join(" ");
-  };
-
-  return (
-    <Container column>
-      <Text size={12}>{titleizeText(item.name)}</Text>
-
-      <Container row>
-        <Text color="helper" size={10}>
-          {truncateText(item.value, 42)}
-        </Text>
-        <CopyToClipboard text={item.value.toString()}>
-          <CopyIcon src={copy} alt="copy" />
-        </CopyToClipboard>
-      </Container>
-    </Container>
-  );
-};
-
-export default DatabaseHeaderItem;
-
-const CopyIcon = styled.img`
-  cursor: pointer;
-  margin-left: 5px;
-  margin-right: 5px;
-  width: 10px;
-  height: 10px;
-`;

+ 0 - 127
dashboard/src/main/home/database-dashboard/tabs/ConnectedAppsTab.tsx

@@ -1,127 +0,0 @@
-import React, { useContext, useMemo, useState } from "react";
-import _ from "lodash";
-import { useHistory } from "react-router";
-import styled from "styled-components";
-
-import Container from "components/porter/Container";
-import Spacer from "components/porter/Spacer";
-import Text from "components/porter/Text";
-import SelectableAppList from "main/home/app-dashboard/apps/SelectableAppList";
-import { useDatastore } from "lib/hooks/useDatastore";
-import { useLatestAppRevisions } from "lib/hooks/useLatestAppRevisions";
-
-import { Context } from "shared/Context";
-
-import { useDatastoreContext } from "../DatabaseContextProvider";
-import ConnectAppsModal from "../shared/ConnectAppsModal";
-
-const ConnectedAppsTab: React.FC = () => {
-  const [showConnectAppsModal, setShowConnectAppsModal] = useState(false);
-  const { projectId, datastore } = useDatastoreContext();
-  // NB: the cluster id here is coming from the global context, but really it should be coming from
-  // the datastore context. However, we do not currently have a way to relate db to the cluster it lives in.
-  // This will be a bug for multi-cluster projects.
-  const { currentCluster: { id: clusterId = 0 } = {} } = useContext(Context);
-  const { revisions } = useLatestAppRevisions({
-    projectId,
-    clusterId,
-  });
-  const { attachDatastoreToAppInstances } = useDatastore();
-  const history = useHistory();
-
-  const { connectedApps, remainingApps } = useMemo(() => {
-    const [connected, remaining] = _.partition(
-      revisions,
-      (r) => datastore.env?.linked_applications.includes(r.source.name)
-    );
-    return {
-      connectedApps: connected.sort((a, b) =>
-        a.source.name.localeCompare(b.source.name)
-      ),
-      remainingApps: remaining.sort((a, b) =>
-        a.source.name.localeCompare(b.source.name)
-      ),
-    };
-  }, [revisions, datastore.env?.linked_applications]);
-
-  return (
-    <ConnectedAppsContainer>
-      <Container row>
-        <Text size={16}>Connected Apps</Text>
-      </Container>
-      <Spacer y={0.25} />
-      <SelectableAppList
-        appListItems={connectedApps.map((ra) => ({
-          app: ra,
-          key: ra.source.name,
-          onSelect: () => {
-            history.push(
-              `/apps/${ra.source.name}?target=${ra.app_revision.deployment_target.id}`
-            );
-          },
-        }))}
-      />
-      <Spacer y={0.25} />
-      <AddAddonButton
-        onClick={() => {
-          setShowConnectAppsModal(true);
-        }}
-      >
-        <I className="material-icons add-icon">add</I>
-        Connect apps to this datastore
-      </AddAddonButton>
-      {showConnectAppsModal && (
-        <ConnectAppsModal
-          closeModal={() => {
-            setShowConnectAppsModal(false);
-          }}
-          apps={remainingApps}
-          onSubmit={async (appInstanceIds: string[]) => {
-            await attachDatastoreToAppInstances({
-              name: datastore.name,
-              clusterId,
-              appInstanceIds,
-            });
-          }}
-        />
-      )}
-    </ConnectedAppsContainer>
-  );
-};
-
-export default ConnectedAppsTab;
-
-const ConnectedAppsContainer = styled.div`
-  width: 100%;
-`;
-
-const AddAddonButton = styled.div`
-  color: #aaaabb;
-  background: ${({ theme }) => theme.fg};
-  border: 1px solid #494b4f;
-  :hover {
-    border: 1px solid #7a7b80;
-    color: white;
-  }
-  display: flex;
-  align-items: center;
-  border-radius: 5px;
-  height: 40px;
-  font-size: 13px;
-  width: 100%;
-  padding-left: 10px;
-  cursor: pointer;
-  .add-icon {
-    width: 30px;
-    font-size: 20px;
-  }
-`;
-
-const I = styled.i`
-  color: white;
-  font-size: 14px;
-  display: flex;
-  align-items: center;
-  margin-right: 7px;
-  justify-content: center;
-`;

+ 0 - 34
dashboard/src/main/home/database-dashboard/tabs/MetricsTab.tsx

@@ -1,34 +0,0 @@
-import React, { useEffect, useState } from "react";
-import styled from "styled-components";
-
-type Props = {
-};
-
-const MetricsTab: React.FC<Props> = ({
-}) => {
-    const [isExpanded, setIsExpanded] = useState(false);
-
-    useEffect(() => {
-        // Do something
-    }, []);
-
-    return (
-        <StyledTemplateComponent>
-        </StyledTemplateComponent>
-    );
-};
-
-export default MetricsTab;
-
-const StyledTemplateComponent = styled.div`
-width: 100%;
-animation: fadeIn 0.3s 0s;
-@keyframes fadeIn {
-  from {
-    opacity: 0;
-  }
-  to {
-    opacity: 1;
-  }
-}
-`;

+ 0 - 23
dashboard/src/main/home/database-dashboard/utils.tsx

@@ -1,23 +0,0 @@
-import { type SerializedDatastore } from "lib/databases/types";
-
-export const datastoreField = (
-  datastore: SerializedDatastore,
-  field: string
-): string => {
-  if (datastore.metadata?.length === 0) {
-    return "";
-  }
-
-  const properties = datastore.metadata?.filter(
-    (metadata) => metadata.name === field
-  );
-  if (properties === undefined || properties.length === 0) {
-    return "";
-  }
-
-  if (properties.length === 0) {
-    return "";
-  }
-
-  return properties[0].value;
-};

+ 0 - 30
dashboard/src/main/home/env-dashboard/EnvGroupList.tsx

@@ -1,30 +0,0 @@
-import React from "react";
-import styled from "styled-components";
-
-import EnvGroup from "./EnvGroup";
-import { type RouteComponentProps, withRouter } from "react-router";
-
-type Props = RouteComponentProps & {
-  envGroups: any[];
-};
-
-const EnvGroupList: React.FunctionComponent<Props> = ({ envGroups }) => {
-  return (
-    <StyledEnvGroupList>
-      {envGroups.map((envGroup: any, i: number) => {
-        return (
-          <EnvGroup
-            key={i}
-            envGroup={envGroup}
-          />
-        );
-      })}
-    </StyledEnvGroupList>
-  );
-};
-
-export default withRouter(EnvGroupList);
-
-const StyledEnvGroupList = styled.div`
-  padding-bottom: 85px;
-`;

+ 0 - 243
dashboard/src/main/home/infrastructure/ExpandedInfra.tsx

@@ -1,243 +0,0 @@
-import React, { useContext, useEffect, useState } from "react";
-import styled from "styled-components";
-import DynamicLink from "components/DynamicLink";
-import Loading from "components/Loading";
-import { Context } from "shared/Context";
-import api from "shared/api";
-import { integrationList } from "shared/common";
-
-import DeployList from "./components/DeployList";
-import InfraResourceList from "./components/InfraResourceList";
-import PorterFormWrapper from "components/porter-form/PorterFormWrapper";
-import { readableDate } from "shared/string_utils";
-import Placeholder from "components/OldPlaceholder";
-import Header from "components/expanded-object/Header";
-import { Infrastructure, KindMap, Operation } from "shared/types";
-import InfraSettings from "./components/InfraSettings";
-import { useParams } from "react-router";
-
-type InfraTabOptions = "deploys" | "resources" | "settings";
-
-type ExpandedInfraParams = {
-  infra_id_str: string;
-};
-
-const ExpandedInfra: React.FunctionComponent = () => {
-  const { infra_id_str } = useParams<ExpandedInfraParams>();
-  const infra_id = parseInt(infra_id_str);
-  const [infra, setInfra] = useState<Infrastructure>(null);
-  const [infraForm, setInfraForm] = useState<any>(null);
-  const [saveValuesStatus, setSaveValueStatus] = useState<string>(null);
-  const [hasError, setHasError] = useState(false);
-
-  const { currentProject, setCurrentError } = useContext(Context);
-
-  useEffect(() => {
-    if (!currentProject) {
-      return;
-    }
-
-    refreshInfra();
-  }, [currentProject, infra_id]);
-
-  const refreshInfra = () => {
-    api
-      .getInfraByID(
-        "<token>",
-        {},
-        {
-          project_id: currentProject.id,
-          infra_id: infra_id,
-        }
-      )
-      .then(({ data }) => {
-        setInfra(data);
-      })
-      .catch((err) => {
-        console.error(err);
-        setHasError(true);
-        setCurrentError(err.response?.data?.error);
-      });
-  };
-
-  useEffect(() => {
-    if (!currentProject || !infra) {
-      return;
-    }
-
-    api
-      .getOperation(
-        "<token>",
-        {},
-        {
-          project_id: currentProject.id,
-          infra_id: infra_id,
-          operation_id: infra.latest_operation.id,
-        }
-      )
-      .then(({ data }) => {
-        setInfraForm(data.form);
-      })
-      .catch((err) => {
-        console.error(err);
-        setHasError(true);
-        setCurrentError(err.response?.data?.error);
-      });
-  }, [currentProject, infra, infra?.latest_operation?.id]);
-
-  const update = (values: any, cb: () => void) => {
-    setSaveValueStatus("loading");
-
-    api
-      .updateInfra(
-        "<token>",
-        {
-          values: values,
-        },
-        {
-          project_id: currentProject.id,
-          infra_id: infra.id,
-        }
-      )
-      .then(({ data }) => {
-        // the resulting data is now the latest operation
-        let newInfra = infra;
-        newInfra.latest_operation = data;
-        setInfra(newInfra);
-        setSaveValueStatus("successful");
-        cb();
-      })
-      .catch((err) => {
-        console.error(err);
-      });
-  };
-
-  const setLatestOperation = (operation: Operation) => {
-    let newInfra = infra;
-    newInfra.latest_operation = operation;
-    setInfra(newInfra);
-  };
-
-  if (hasError) {
-    return <Placeholder>Error loading infra</Placeholder>;
-  }
-
-  if (!infra || !infraForm) {
-    return <Loading />;
-  }
-
-  const renderTabContents = (newTab: InfraTabOptions) => {
-    switch (newTab) {
-      case "deploys":
-        return (
-          <DeployList
-            infra={infra}
-            setLatestOperation={setLatestOperation}
-            refreshInfra={refreshInfra}
-          />
-        );
-      case "resources":
-        return <InfraResourceList infra_id={infra_id} />;
-      case "settings":
-        return <InfraSettings infra_id={infra_id} onDelete={refreshInfra} />;
-    }
-  };
-
-  return (
-    <StyledExpandedInfra>
-      <Header
-        last_updated={readableDate(infra.latest_operation?.last_updated)}
-        back_link={"/infrastructure"}
-        name={integrationList[infra.kind]?.label}
-        icon={integrationList[infra.kind]?.icon}
-        inline_title_items={[
-          <ResourceLink
-            key="resource_link"
-            to={KindMap[infra.kind].resource_link}
-            target="_blank"
-            onClick={(e) => e.stopPropagation()}
-          >
-            {KindMap[infra.kind].resource_name}
-            <i className="material-icons">open_in_new</i>
-          </ResourceLink>,
-        ]}
-      />
-      <PorterFormContainer>
-        <PorterFormWrapper
-          showStateDebugger={false}
-          formData={infraForm}
-          valuesToOverride={{}}
-          isReadOnly={false}
-          onSubmit={update}
-          leftTabOptions={[
-            {
-              value: "deploys",
-              label: "Deploys",
-            },
-            {
-              value: "resources",
-              label: "Resources",
-            },
-          ]}
-          rightTabOptions={[
-            {
-              value: "settings",
-              label: "Settings",
-            },
-          ]}
-          renderTabContents={renderTabContents}
-          isInModal={false}
-          hideBottomSpacer={false}
-          saveButtonText={"Update"}
-          saveValuesStatus={saveValuesStatus}
-          redirectTabAfterSave={"deploys"}
-        />
-      </PorterFormContainer>
-    </StyledExpandedInfra>
-  );
-};
-
-export default ExpandedInfra;
-
-const PorterFormContainer = styled.div`
-  position: relative;
-  min-width: 300px;
-`;
-
-const StyledExpandedInfra = styled.div`
-  width: 100%;
-  z-index: 0;
-  animation: fadeIn 0.3s;
-  animation-timing-function: ease-out;
-  animation-fill-mode: forwards;
-  display: flex;
-  flex-direction: column;
-
-  @keyframes fadeIn {
-    from {
-      opacity: 0;
-    }
-    to {
-      opacity: 1;
-    }
-  }
-`;
-
-const ResourceLink = styled(DynamicLink)`
-  font-size: 13px;
-  font-weight: 400;
-  margin-left: 20px;
-  color: #aaaabb;
-  display: flex;
-  align-items: center;
-
-  :hover {
-    text-decoration: underline;
-    color: white;
-  }
-
-  > i {
-    margin-left: 7px;
-    font-size: 17px;
-  }
-`;

+ 0 - 337
dashboard/src/main/home/infrastructure/InfrastructureList.tsx

@@ -1,337 +0,0 @@
-import DynamicLink from "components/DynamicLink";
-import React, { useContext, useEffect, useMemo, useState } from "react";
-import { Context } from "shared/Context";
-import api from "shared/api";
-import { useHistory, useLocation } from "react-router";
-import { pushFiltered } from "shared/routing";
-
-import { Column } from "react-table";
-import styled from "styled-components";
-import Table from "components/OldTable";
-
-import Loading from "components/Loading";
-
-import _ from "lodash";
-import { integrationList } from "shared/common";
-import { Infrastructure, KindMap } from "shared/types";
-import { capitalize, readableDate } from "shared/string_utils";
-import Placeholder from "components/OldPlaceholder";
-import SaveButton from "components/SaveButton";
-import { useRouting } from "shared/routing";
-import Description from "components/Description";
-
-const InfrastructureList = () => {
-  const [isLoading, setIsLoading] = useState(true);
-  const [hasError, setHasError] = useState(false);
-  const [infraList, setInfraList] = useState<Infrastructure[]>([]);
-  const { currentProject, setCurrentError } = useContext(Context);
-  const { pushFiltered } = useRouting();
-
-  useEffect(() => {
-    if (currentProject) {
-      let isSubscribed = true;
-
-      api
-        .getInfra(
-          "<token>",
-          {
-            version: "v2",
-          },
-          {
-            project_id: currentProject.id,
-          }
-        )
-        .then(({ data }) => {
-          if (!isSubscribed) {
-            return;
-          }
-
-          if (!Array.isArray(data)) {
-            throw Error("Data is not an array");
-          }
-
-          setInfraList(data);
-          setIsLoading(false);
-        })
-        .catch((err) => {
-          console.error(err);
-
-          setHasError(true);
-          setCurrentError(err.response?.data?.error);
-          setIsLoading(false);
-        });
-
-      return () => {
-        isSubscribed = false;
-      };
-    }
-  }, [currentProject]);
-
-  const columns = useMemo<Array<Column<Infrastructure>>>(() => {
-    if (infraList.length == 0) {
-      return [];
-    }
-
-    return [
-      {
-        Header: "Kind",
-        accessor: "kind",
-        Cell: ({ row }) => {
-          let original = row.original as Infrastructure;
-
-          return (
-            <KindContainer>
-              <Icon
-                src={
-                  integrationList[original.kind]?.icon ||
-                  integrationList["dockerhub"].icon
-                }
-              />
-              <Kind>{integrationList[original.kind]?.label}</Kind>
-            </KindContainer>
-          );
-        },
-      },
-      {
-        Header: "Name",
-        accessor: "name",
-      },
-      {
-        Header: "Status",
-        accessor: "status",
-        Cell: ({ row }) => {
-          let original = row.original as Infrastructure;
-
-          return (
-            <Status>
-              <StatusDot status={original.status} />
-              {capitalize(original.status)}
-            </Status>
-          );
-        },
-      },
-      {
-        Header: "Resource",
-        accessor: "aws_integration_id",
-        Cell: ({ row }) => {
-          let original = row.original as Infrastructure;
-
-          return (
-            <ResourceLink
-              to={KindMap[original.kind].resource_link}
-              target="_blank"
-              onClick={(e) => e.stopPropagation()}
-            >
-              {KindMap[original.kind].resource_name}
-              <i className="material-icons">open_in_new</i>
-            </ResourceLink>
-          );
-        },
-      },
-      {
-        Header: "Last Updated",
-        accessor: "updated_at",
-        Cell: ({ row }) => {
-          return readableDate(row.original.updated_at);
-        },
-      },
-      {
-        Header: "Version",
-        accessor: "source_version",
-      },
-    ];
-  }, [infraList]);
-
-  if (isLoading) {
-    return (
-      <Placeholder>
-        <Loading />
-      </Placeholder>
-    );
-  }
-
-  if (hasError) {
-    return <Placeholder>Error</Placeholder>;
-  }
-
-  const renderTable = () => {
-    if (infraList.length == 0) {
-      return <Placeholder>No infrastructure found.</Placeholder>;
-    }
-
-    return (
-      <Table
-        columns={columns}
-        data={infraList}
-        isLoading={isLoading}
-        onRowClick={(row) => {
-          let original = row.original as Infrastructure;
-          pushFiltered(`/infrastructure/${original.id}`, ["project_id"]);
-        }}
-      />
-    );
-  };
-
-  return (
-    <DatabasesListWrapper>
-      <StyledTitleSection>
-        <DashboardIcon>
-          <i className="material-icons">build_circle</i>
-        </DashboardIcon>
-        Managed infrastructure (legacy)
-      </StyledTitleSection>
-      <InfoSection>
-        <Description>
-          Managed infrastructure is infrastructure controlled and updated
-          through Porter.
-        </Description>
-      </InfoSection>
-      <LineBreak />
-      <ControlRow>
-        <SaveButtonContainer>
-          <SaveButton
-            makeFlush={true}
-            clearPosition={true}
-            onClick={() =>
-              pushFiltered(`/infrastructure/provision`, ["project_id"])
-            }
-          >
-            <i className="material-icons">add</i>
-            Create Infrastructure
-          </SaveButton>
-        </SaveButtonContainer>
-      </ControlRow>
-      <StyledTableWrapper>{renderTable()}</StyledTableWrapper>
-    </DatabasesListWrapper>
-  );
-};
-
-export default InfrastructureList;
-
-const KindContainer = styled.div`
-  display: flex;
-  align-items: center;
-  min-width: 250px;
-`;
-
-const Kind = styled.div`
-  margin-left: 8px;
-`;
-
-const DatabasesListWrapper = styled.div`
-  margin-top: 35px;
-`;
-
-const StyledTableWrapper = styled.div`
-  padding: 14px;
-  position: relative;
-  border-radius: 8px;
-  background: #26292e;
-  border: 1px solid #494b4f;
-  width: 100%;
-  height: 100%;
-  :not(:last-child) {
-    margin-bottom: 25px;
-  }
-`;
-
-const ControlRow = styled.div`
-  display: flex;
-  margin-left: auto;
-  justify-content: space-between;
-  align-items: center;
-  margin-bottom: 35px;
-  padding-left: 0px;
-`;
-
-const Icon = styled.img`
-  height: 20px;
-`;
-
-const DashboardIcon = styled.div`
-  height: 45px;
-  min-width: 45px;
-  width: 45px;
-  border-radius: 5px;
-  margin-right: 17px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  background: #676c7c;
-  border: 2px solid #8e94aa;
-  > i {
-    font-size: 22px;
-  }
-`;
-
-const InfoSection = styled.div`
-  margin-top: 36px;
-  font-family: "Work Sans", sans-serif;
-  margin-left: 0px;
-  margin-bottom: 35px;
-`;
-
-const LineBreak = styled.div`
-  width: calc(100% - 0px);
-  height: 1px;
-  background: #494b4f;
-  margin: 10px 0px 35px;
-`;
-
-const StyledTitleSection = styled.div`
-  margin-bottom: 15px;
-  display: flex;
-  align-items: center;
-  justify-content: start;
-  font-size: 24px;
-  font-weight: 600;
-`;
-
-const Status = styled.span`
-  font-size: 13px;
-  display: flex;
-  align-items: center;
-  margin-left: 1px;
-  min-height: 17px;
-  color: #a7a6bb;
-`;
-
-const StatusDot = styled.div`
-  width: 8px;
-  height: 8px;
-  background: ${(props: { status: string }) =>
-    props.status === "created"
-      ? "#4797ff"
-      : props.status === "failed"
-      ? "#ed5f85"
-      : props.status === "completed"
-      ? "#00d12a"
-      : "#f5cb42"};
-  border-radius: 20px;
-  margin-left: 3px;
-  margin-right: 15px;
-`;
-
-const ResourceLink = styled(DynamicLink)`
-  font-size: 13px;
-  font-weight: 400;
-  margin-left: 7px;
-  color: #aaaabb;
-  display: flex;
-  align-items: center;
-
-  :hover {
-    text-decoration: underline;
-    color: white;
-  }
-
-  > i {
-    margin-left: 7px;
-    font-size: 17px;
-  }
-`;
-
-const SaveButtonContainer = styled.div`
-  position: relative;
-  width: 100%;
-`;

+ 0 - 39
dashboard/src/main/home/infrastructure/InfrastructureRouter.tsx

@@ -1,39 +0,0 @@
-import React, { useContext, useLayoutEffect } from "react";
-import { Route, Switch } from "react-router-dom";
-import { withRouter } from "react-router";
-import { useParams } from "react-router-dom";
-import InfrastructureList from "./InfrastructureList";
-import ExpandedInfra from "./ExpandedInfra";
-import ProvisionInfra from "./components/ProvisionInfra";
-import { Context } from "shared/Context";
-import { useRouting } from "shared/routing";
-
-const InfrastructureRouter = () => {
-  const { currentCluster, currentProject } = useContext(Context);
-  const { pushFiltered } = useRouting();
-
-  useLayoutEffect(() => {
-    if (!currentProject || !currentProject.managed_infra_enabled) {
-      pushFiltered("/dashboard", []);
-    }
-  }, [currentProject]);
-
-  return (
-    <Switch>
-      <Route path="/infrastructure/provision/:name">
-        <ProvisionInfra />
-      </Route>
-      <Route path="/infrastructure/provision">
-        <ProvisionInfra />
-      </Route>
-      <Route path="/infrastructure/:infra_id_str">
-        <ExpandedInfra />
-      </Route>
-      <Route path="/infrastructure">
-        <InfrastructureList />
-      </Route>
-    </Switch>
-  );
-};
-
-export default withRouter(InfrastructureRouter);

+ 0 - 342
dashboard/src/main/home/infrastructure/components/DeployList.tsx

@@ -1,342 +0,0 @@
-import React, { useContext, useEffect, useState } from "react";
-import { Context } from "shared/Context";
-import api from "shared/api";
-import styled from "styled-components";
-import Loading from "components/Loading";
-import {
-  Infrastructure,
-  Operation,
-  OperationStatus,
-  OperationType,
-} from "shared/types";
-import { readableDate } from "shared/string_utils";
-import Placeholder from "components/OldPlaceholder";
-import { useWebsockets } from "shared/hooks/useWebsockets";
-import ExpandedOperation from "./ExpandedOperation";
-
-type Props = {
-  infra: Infrastructure;
-  setLatestOperation: (operation: Operation) => void;
-  refreshInfra: () => void;
-};
-
-const DeployList: React.FunctionComponent<Props> = ({
-  infra,
-  setLatestOperation,
-  refreshInfra,
-}) => {
-  const [isLoading, setIsLoading] = useState(true);
-  const [hasError, setHasError] = useState(false);
-  const [operationList, setOperationList] = useState<Operation[]>([]);
-  const [selectedOperation, setSelectedOperation] = useState<Operation>(null);
-  const { currentProject, setCurrentError } = useContext(Context);
-
-  const refreshOperationList = () => {
-    api
-      .listOperations(
-        "<token>",
-        {},
-        {
-          project_id: currentProject.id,
-          infra_id: infra.id,
-        }
-      )
-      .then(({ data }) => {
-        if (!Array.isArray(data)) {
-          throw Error("Data is not an array");
-        }
-
-        setOperationList(data);
-        setIsLoading(false);
-      })
-      .catch((err) => {
-        console.error(err);
-        setHasError(true);
-        setCurrentError(err.response?.data?.error);
-        setIsLoading(false);
-      });
-  };
-
-  useEffect(() => {
-    refreshOperationList();
-  }, [currentProject, infra, infra?.latest_operation?.id]);
-
-  const { newWebsocket, openWebsocket, closeWebsocket } = useWebsockets();
-
-  const parseOperationWebsocketEvent = (evt: MessageEvent) => {
-    let { status } = JSON.parse(evt.data);
-
-    // if the status is operation completed, mark that operation as completed
-    if (status == "OPERATION_COMPLETED") {
-      refreshOperationList();
-    }
-  };
-
-  const setupOperationWebsocket = (websocketID: string) => {
-    let apiPath = `/api/projects/${currentProject.id}/infras/${infra.id}/operations/${infra.latest_operation.id}/state`;
-
-    const wsConfig = {
-      onopen: () => {
-        console.log(`connected to websocket:`, websocketID);
-      },
-      onmessage: parseOperationWebsocketEvent,
-      onclose: () => {
-        console.log(`closing websocket:`, websocketID);
-      },
-      onerror: (err: ErrorEvent) => {
-        console.log(err);
-        closeWebsocket(websocketID);
-      },
-    };
-
-    newWebsocket(websocketID, apiPath, wsConfig);
-    openWebsocket(websocketID);
-  };
-
-  useEffect(() => {
-    if (!currentProject || !infra || !infra.latest_operation) {
-      return;
-    }
-
-    // if the operation list is empty or does not match the latest infra operation, don't
-    // open a websocket
-    if (
-      operationList.length == 0 ||
-      infra.latest_operation.id !== operationList[0].id
-    ) {
-      return;
-    }
-
-    // if the latest_operation is in progress, open a websocket
-    if (operationList[0].status === "starting") {
-      const websocketID = operationList[0].id + "_state";
-
-      setupOperationWebsocket(websocketID);
-
-      return () => {
-        closeWebsocket(websocketID);
-      };
-    }
-  }, [currentProject, infra, operationList]);
-
-  if (isLoading) {
-    return (
-      <Placeholder>
-        <Loading />
-      </Placeholder>
-    );
-  }
-
-  if (operationList.length == 0) {
-    return <Placeholder>No operations available</Placeholder>;
-  }
-
-  if (hasError) {
-    return <Placeholder>Error</Placeholder>;
-  }
-
-  const getOperationDescription = (
-    type: OperationType,
-    status: OperationStatus
-  ): string => {
-    switch (type) {
-      case "retry_create":
-      case "create":
-        if (status == "starting") {
-          return "Infrastructure creation in progress";
-        } else if (status == "completed") {
-          return "Infrastructure creation completed.";
-        } else if (status == "errored") {
-          return "This infrastructure encountered an error while creating.";
-        }
-      case "update":
-        if (status == "starting") {
-          return "Infrastructure update in progress";
-        } else if (status == "completed") {
-          return "Infrastructure update completed.";
-        } else if (status == "errored") {
-          return "This infrastructure encountered an error while updating.";
-        }
-      case "retry_delete":
-      case "delete":
-        if (status == "starting") {
-          return "Infrastructure deletion in progress";
-        } else if (status == "completed") {
-          return "Infrastructure deletion completed.";
-        } else if (status == "errored") {
-          return "This infrastructure encountered an error while deleting.";
-        }
-    }
-  };
-
-  const backFromExpandedOperation = (operation?: Operation) => {
-    if (operation) {
-      setLatestOperation(operation);
-    }
-
-    setSelectedOperation(null);
-  };
-
-  const renderContents = () => {
-    if (selectedOperation) {
-      return (
-        <ExpandedOperation
-          operation_id={selectedOperation.id}
-          infra={infra}
-          back={backFromExpandedOperation}
-          refreshInfra={refreshInfra}
-        />
-      );
-    }
-
-    return operationList.map((operation, i) => {
-      return (
-        <React.Fragment key={i}>
-          <StyledCard
-            status={operation.status}
-            onClick={() => setSelectedOperation(operation)}
-          >
-            <ContentContainer>
-              <Icon
-                status={operation.status}
-                className="material-icons-outlined"
-              >
-                {operation.status === "errored"
-                  ? "report_problem"
-                  : operation.status === "completed"
-                  ? "check_circle"
-                  : "cached"}
-              </Icon>
-              <DeployInformation>
-                <DeployHeader>
-                  {getOperationDescription(operation.type, operation.status)}
-                </DeployHeader>
-              </DeployInformation>
-            </ContentContainer>
-            <MetaContainer>
-              <TimestampContainer>
-                <TimestampIcon className="material-icons-outlined">
-                  access_time
-                </TimestampIcon>
-                <span>{readableDate(operation.last_updated)}</span>
-              </TimestampContainer>
-            </MetaContainer>
-            <NextIconContainer>
-              <i className="material-icons next-icon">navigate_next</i>
-            </NextIconContainer>
-          </StyledCard>
-        </React.Fragment>
-      );
-    });
-  };
-
-  return (
-    <DatabasesListWrapper>
-      <StyledDeploysGrid>{renderContents()}</StyledDeploysGrid>
-    </DatabasesListWrapper>
-  );
-};
-
-export default DeployList;
-
-const DatabasesListWrapper = styled.div``;
-
-const StyledDeploysGrid = styled.div`
-  display: grid;
-  grid-row-gap: 15px;
-  grid-template-columns: 1;
-`;
-
-const StyledCard = styled.div<{ status: string }>`
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  border: 1px solid
-    ${({ status }) => (status === "critical" ? "#ff385d" : "#ffffff44")};
-  background: #ffffff08;
-  margin-bottom: 3px;
-  border-radius: 10px;
-  padding: 14px;
-  overflow: hidden;
-  height: 60px;
-  font-size: 13px;
-  cursor: pointer;
-  :hover {
-    background: #ffffff11;
-    border: 1px solid
-      ${({ status }) => (status === "critical" ? "#ff385d" : "#ffffff66")};
-  }
-  animation: fadeIn 0.5s;
-  @keyframes fadeIn {
-    from {
-      opacity: 0;
-    }
-    to {
-      opacity: 1;
-    }
-  }
-
-  .next-icon {
-    display: none;
-    color: #ffffff55;
-  }
-
-  :hover .next-icon {
-    display: inline-block;
-  }
-`;
-
-const NextIconContainer = styled.div`
-  width: 30px;
-  padding-top: 2px;
-`;
-
-const ContentContainer = styled.div`
-  display: flex;
-  height: 100%;
-  width: 100%;
-  align-items: center;
-`;
-
-const Icon = styled.span<{ status: OperationStatus }>`
-  font-size: 20px;
-  margin-left: 10px;
-  margin-right: 20px;
-  color: ${({ status }) => (status === "errored" ? "#ff385d" : "#aaaabb")};
-`;
-
-const DeployInformation = styled.div`
-  display: flex;
-  flex-direction: column;
-  justify-content: space-around;
-  height: 100%;
-`;
-
-const DeployHeader = styled.div`
-  font-family: "Work Sans", sans-serif;
-  font-weight: 500;
-  color: #ffffff;
-`;
-
-const MetaContainer = styled.div`
-  display: flex;
-  align-items: center;
-  white-space: nowrap;
-  height: 100%;
-`;
-
-const TimestampContainer = styled.div`
-  display: flex;
-  white-space: nowrap;
-  align-items: center;
-  color: #ffffff55;
-  margin-right: 10px;
-  font-size: 13px;
-  min-width: 130px;
-  justify-content: space-between;
-`;
-
-const TimestampIcon = styled.span`
-  margin-right: 7px;
-  font-size: 18px;
-`;

+ 0 - 401
dashboard/src/main/home/infrastructure/components/ExpandedOperation.tsx

@@ -1,401 +0,0 @@
-import React, { useContext, useEffect, useState } from "react";
-import { Context } from "shared/Context";
-import api from "shared/api";
-import styled from "styled-components";
-import Loading from "components/Loading";
-import {
-  Infrastructure,
-  Operation,
-  OperationStatus,
-  OperationType,
-} from "shared/types";
-import { readableDate } from "shared/string_utils";
-import Placeholder from "components/OldPlaceholder";
-import { useWebsockets } from "shared/hooks/useWebsockets";
-import Heading from "components/form-components/Heading";
-import SaveButton from "components/SaveButton";
-import PorterFormWrapper from "components/porter-form/PorterFormWrapper";
-import Description from "components/Description";
-import { OperationDetails } from "components/ProvisionerStatus";
-
-type Props = {
-  infra: Infrastructure;
-  operation_id: string;
-  back: (operation?: Operation) => void;
-  refreshInfra: () => void;
-};
-
-const ExpandedOperation: React.FunctionComponent<Props> = ({
-  infra,
-  operation_id,
-  back,
-  refreshInfra,
-}) => {
-  const [isLoading, setIsLoading] = useState(true);
-  const [hasError, setHasError] = useState(false);
-  const [operation, setOperation] = useState<Operation>(null);
-  const [logs, setLogs] = useState<string[]>(null);
-  const { currentProject, setCurrentError } = useContext(Context);
-
-  const { newWebsocket, openWebsocket, closeWebsocket } = useWebsockets();
-
-  useEffect(() => {
-    api
-      .getOperation(
-        "<token>",
-        {},
-        {
-          project_id: currentProject.id,
-          infra_id: infra.id,
-          operation_id: operation_id,
-        }
-      )
-      .then(({ data }) => {
-        setOperation(data);
-        setIsLoading(false);
-      })
-      .catch((err) => {
-        console.error(err);
-        setHasError(true);
-        setCurrentError(err.response?.data?.error);
-        setIsLoading(false);
-      });
-  }, [currentProject, operation_id]);
-
-  const parseLogWebsocketEvent = (evt: MessageEvent) => {
-    setLogs((logs) => {
-      if (!logs) {
-        return [evt.data];
-      }
-
-      let newLogs = [...logs];
-      newLogs.push(evt.data);
-      return newLogs;
-    });
-  };
-
-  const setupLogWebsocket = (websocketID: string) => {
-    let apiPath = `/api/projects/${currentProject.id}/infras/${operation.infra_id}/operations/${operation.id}/log_stream`;
-
-    const wsConfig = {
-      onopen: () => {
-        console.log(`connected to websocket:`, websocketID);
-      },
-      onmessage: parseLogWebsocketEvent,
-      onclose: () => {
-        console.log(`closing websocket:`, websocketID);
-      },
-      onerror: (err: ErrorEvent) => {
-        console.log(err);
-        closeWebsocket(websocketID);
-      },
-    };
-
-    newWebsocket(websocketID, apiPath, wsConfig);
-    openWebsocket(websocketID);
-  };
-
-  useEffect(() => {
-    if (!currentProject || !operation) {
-      return;
-    }
-
-    // if the operation is in progress, open a websocket
-    if (operation.status === "starting") {
-      const websocketID = operation.id + "_log_stream";
-
-      setupLogWebsocket(websocketID);
-
-      return () => {
-        closeWebsocket(websocketID);
-      };
-    } else {
-      // if the operation is completed, get logs from the endpoint
-      api
-        .getOperationLogs(
-          "<token>",
-          {},
-          {
-            project_id: currentProject.id,
-            infra_id: infra.id,
-            operation_id: operation_id,
-          }
-        )
-        .then(({ data }) => {
-          if (!Array.isArray(data.logs)) {
-            throw Error("Data is not an array");
-          }
-
-          setLogs(data.logs);
-        })
-        .catch(() => {
-          setLogs(["No logs available."]);
-        });
-    }
-  }, [currentProject, operation]);
-
-  const retry = () => {
-    let pathParams = {
-      project_id: currentProject.id,
-      infra_id: infra.id,
-    };
-
-    let apiCall = api.updateInfra;
-
-    if (operation.type == "create" || operation.type == "retry_create") {
-      apiCall = api.retryCreateInfra;
-    } else if (operation.type == "delete" || operation.type == "retry_delete") {
-      apiCall = api.retryDeleteInfra;
-    }
-
-    apiCall("<token>", {}, pathParams)
-      .then(({ data }) => {
-        back(data);
-      })
-      .catch((err) => {
-        console.error(err);
-        setHasError(true);
-        setCurrentError(err.response?.data?.error);
-      });
-  };
-
-  if (isLoading) {
-    return (
-      <Placeholder>
-        <Loading />
-      </Placeholder>
-    );
-  }
-
-  if (hasError) {
-    return <Placeholder>Error</Placeholder>;
-  }
-
-  const getOperationDescription = (
-    type: OperationType,
-    status: OperationStatus,
-    time: string
-  ): string => {
-    switch (type) {
-      case "retry_create":
-      case "create":
-        if (status == "starting") {
-          return (
-            "Infrastructure creation in progress, started at " +
-            readableDate(time)
-          );
-        } else if (status == "completed") {
-          return "Infrastructure creation completed at " + readableDate(time);
-        } else if (status == "errored") {
-          return "This infrastructure encountered an error while creating.";
-        }
-      case "update":
-        if (status == "starting") {
-          return (
-            "Infrastructure update in progress, started at " +
-            readableDate(time)
-          );
-        } else if (status == "completed") {
-          return "Infrastructure update completed at " + readableDate(time);
-        } else if (status == "errored") {
-          return "This infrastructure encountered an error while updating.";
-        }
-      case "retry_delete":
-      case "delete":
-        if (status == "starting") {
-          return (
-            "Infrastructure deletion in progress, started at " +
-            readableDate(time)
-          );
-        } else if (status == "completed") {
-          return "Infrastructure deletion completed at " + readableDate(time);
-        } else if (status == "errored") {
-          return "This infrastructure encountered an error while deleting.";
-        }
-    }
-  };
-
-  const renderRerunButton = () => {
-    let buttonText = "Retry Operation";
-
-    if (operation.type == "create" || operation.type == "retry_create") {
-      buttonText = "Retry Creation";
-    } else if (operation.type == "delete" || operation.type == "retry_delete") {
-      buttonText = "Retry Deletion";
-    } else if (operation.type == "update") {
-      buttonText = "Retry";
-    }
-    return (
-      <SaveButton
-        onClick={retry}
-        text={buttonText}
-        disabled={false}
-        makeFlush={true}
-        clearPosition={true}
-      />
-    );
-  };
-
-  const renderLogs = () => {
-    if (!logs) {
-      return (
-        <Placeholder>
-          <Loading />
-        </Placeholder>
-      );
-    }
-
-    return logs.map((l, i) => <Log key={i}>{l}</Log>);
-  };
-
-  const renderOperationDetails = () => {
-    if (infra.latest_operation.id == operation.id) {
-      return (
-        <>
-          <Description>Infrastructure progress:</Description>
-          <OperationDetails
-            infra={infra}
-            refreshInfra={refreshInfra}
-            useOperation={operation}
-            padding={"12px 0"}
-          />
-        </>
-      );
-    }
-
-    return (
-      <>
-        <Description>
-          {getOperationDescription(
-            operation.type,
-            operation.status,
-            operation.last_updated
-          )}
-        </Description>
-        <Br />
-      </>
-    );
-  };
-
-  return (
-    <StyledCard>
-      <BackArrowContainer>
-        <BackArrow onClick={() => back()}>
-          <i className="material-icons next-icon">navigate_before</i>
-          All Deploys
-        </BackArrow>
-      </BackArrowContainer>
-      <MetadataContainer>
-        <Heading>Deployment Summary</Heading>
-        {renderOperationDetails()}
-        {renderRerunButton()}
-      </MetadataContainer>
-      <MetadataContainer>
-        <Heading>Configuration</Heading>
-        <Description>
-          Your infrastructure was deployed with the following configuration:
-        </Description>
-        <PorterFormContainer>
-          <PorterFormWrapper
-            showStateDebugger={false}
-            formData={operation.form}
-            valuesToOverride={{}}
-            isReadOnly={true}
-            color="#f5cb42"
-            isInModal={false}
-            hideBottomSpacer={false}
-          />
-        </PorterFormContainer>
-      </MetadataContainer>
-      <LogSectionContainer>
-        <LogTitleContainer>
-          <Heading>Deployment Logs</Heading>
-          <Description>
-            The following are the Terraform logs from your deployment:
-          </Description>
-        </LogTitleContainer>
-        <LogContainer>{renderLogs()}</LogContainer>
-      </LogSectionContainer>
-    </StyledCard>
-  );
-};
-
-export default ExpandedOperation;
-
-const PorterFormContainer = styled.div`
-  position: relative;
-  min-width: 300px;
-`;
-
-const Br = styled.div`
-  width: 100%;
-  height: 20px;
-`;
-
-const StyledCard = styled.div`
-  display: grid;
-  grid-row-gap: 15px;
-  grid-template-columns: 1;
-`;
-
-const BackArrowContainer = styled.div`
-  width: 100%;
-  height: 24px;
-`;
-
-const BackArrow = styled.div`
-  > i {
-    color: #aaaabb;
-    font-size: 18px;
-    margin-right: 6px;
-  }
-
-  color: #aaaabb;
-  display: flex;
-  align-items: center;
-  font-size: 14px;
-  cursor: pointer;
-  width: 120px;
-`;
-
-const MetadataContainer = styled.div`
-  margin-bottom: 3px;
-  border-radius: 6px;
-  background: #2e3135;
-  padding: 0 20px 16px 20px;
-  overflow-y: auto;
-  min-height: 180px;
-  font-size: 13px;
-`;
-
-const LogTitleContainer = styled.div`
-  padding: 0 20px;
-  margin-bottom: 20px;
-`;
-
-const LogSectionContainer = styled.div`
-  margin-bottom: 3px;
-  border-radius: 6px;
-  background: #2e3135;
-  overflow: hidden;
-  max-height: 500px;
-  font-size: 13px;
-`;
-
-const LogContainer = styled.div`
-  padding: 14px;
-  font-size: 13px;
-  background: #121318;
-  user-select: text;
-  overflow-wrap: break-word;
-  overflow-y: auto;
-  min-height: 55px;
-  color: #aaaabb;
-  height: 400px;
-`;
-
-const Log = styled.div`
-  font-family: monospace, sans-serif;
-  font-size: 12px;
-  color: white;
-`;

+ 0 - 120
dashboard/src/main/home/infrastructure/components/InfraResourceList.tsx

@@ -1,120 +0,0 @@
-import React, { useContext, useEffect, useState } from "react";
-import { Context } from "shared/Context";
-import api from "shared/api";
-import styled from "styled-components";
-import Loading from "components/Loading";
-import { TFState } from "shared/types";
-import Placeholder from "components/OldPlaceholder";
-
-type Props = {
-  infra_id: number;
-};
-
-const InfraResourceList: React.FunctionComponent<Props> = ({ infra_id }) => {
-  const [isLoading, setIsLoading] = useState(true);
-  const [hasError, setHasError] = useState(false);
-  const [infraState, setInfraState] = useState<TFState>(null);
-  const { currentProject, setCurrentError } = useContext(Context);
-
-  useEffect(() => {
-    api
-      .getInfraState(
-        "<token>",
-        {},
-        {
-          project_id: currentProject.id,
-          infra_id: infra_id,
-        }
-      )
-      .then(({ data }) => {
-        setInfraState(data);
-
-        setIsLoading(false);
-      })
-      .catch((err) => {
-        console.error(err);
-        setHasError(true);
-        setCurrentError(err.response?.data?.error);
-        setIsLoading(false);
-      });
-  }, [currentProject]);
-
-  if (isLoading) {
-    return (
-      <Placeholder>
-        <Loading />
-      </Placeholder>
-    );
-  }
-
-  if (!infraState) {
-    return <Placeholder>No resources available</Placeholder>;
-  }
-
-  if (hasError) {
-    return <Placeholder>Error</Placeholder>;
-  }
-
-  const renderContents = () => {
-    return Object.keys({ ...(infraState?.resources || {}) }).map((key) => {
-      return (
-        <StyledResource key={key} lastItem={false} isSelected={false}>
-          {key}
-        </StyledResource>
-      );
-    });
-  };
-
-  return (
-    <InfraResourceListWrapper>
-      <ListContainer>{renderContents()}</ListContainer>
-    </InfraResourceListWrapper>
-  );
-};
-
-export default InfraResourceList;
-
-const InfraResourceListWrapper = styled.div`
-  width: 100%;
-  height: 100%;
-`;
-
-const ListContainer = styled.div`
-  width: 100%;
-  border-radius: 3px;
-  border: 1px solid #ffffff44;
-  max-height: 400px;
-  background: #ffffff11;
-  overflow-y: auto;
-`;
-
-const StyledResource = styled.div`
-  display: flex;
-  width: 100%;
-  font-size: 13px;
-  border-bottom: 1px solid
-    ${(props: { lastItem: boolean; isSelected: boolean }) =>
-      props.lastItem ? "#00000000" : "#606166"};
-  color: #ffffff;
-  user-select: none;
-  align-items: center;
-  padding: 10px;
-  cursor: pointer;
-  background: #ffffff11;
-  :hover {
-    background: #ffffff22;
-
-    > i {
-      background: #ffffff22;
-    }
-  }
-
-  > img,
-  i {
-    width: 18px;
-    height: 18px;
-    margin-left: 12px;
-    margin-right: 12px;
-    font-size: 20px;
-  }
-`;

+ 0 - 96
dashboard/src/main/home/infrastructure/components/InfraSettings.tsx

@@ -1,96 +0,0 @@
-import React, { useContext, useState } from "react";
-import { Context } from "shared/Context";
-import api from "shared/api";
-import styled from "styled-components";
-import Heading from "components/form-components/Heading";
-import SaveButton from "components/SaveButton";
-import Description from "components/Description";
-import ConfirmOverlay from "components/ConfirmOverlay";
-
-type Props = {
-  infra_id: number;
-  onDelete: () => void;
-};
-
-const InfraSettings: React.FunctionComponent<Props> = ({
-  infra_id,
-  onDelete,
-}) => {
-  const { currentProject, setCurrentError, setCurrentOverlay } = useContext(
-    Context
-  );
-
-  const deleteInfra = () => {
-    api
-      .deleteInfra(
-        "<token>",
-        {},
-        {
-          project_id: currentProject.id,
-          infra_id: infra_id,
-        }
-      )
-      .then(() => {
-        setCurrentOverlay(null);
-        onDelete();
-      })
-      .catch((err) => {
-        console.error(err);
-        setCurrentError(err.response?.data?.error);
-      });
-  };
-
-  return (
-    <>
-      <StyledCard>
-        <MetadataContainer>
-          <Heading>Delete Infrastructure</Heading>
-          <Description>
-            This will destroy all of the existing cloud infrastructure attached
-            to this module.
-          </Description>
-          <Br />
-
-          <SaveButton
-            onClick={() =>
-              setCurrentOverlay({
-                message: `Are you sure you want to delete this infrastructure?`,
-                onYes: deleteInfra,
-                onNo: () => setCurrentOverlay(null),
-              })
-            }
-            text="Delete Infrastructure"
-            color="#b91133"
-            disabled={false}
-            makeFlush={true}
-            clearPosition={true}
-            saveText="Deletion process started, see the Deploys tab for info."
-          />
-        </MetadataContainer>
-      </StyledCard>
-    </>
-  );
-};
-
-export default InfraSettings;
-
-const Br = styled.div`
-  width: 100%;
-  height: 20px;
-`;
-
-const StyledCard = styled.div`
-  display: grid;
-  grid-row-gap: 15px;
-  grid-template-columns: 1;
-`;
-
-const MetadataContainer = styled.div`
-  margin-bottom: 3px;
-  border-radius: 6px;
-  background: #2e3135;
-  padding: 0 20px;
-  overflow-y: auto;
-  min-height: 180px;
-  font-size: 13px;
-`;

+ 0 - 561
dashboard/src/main/home/infrastructure/components/ProvisionInfra.tsx

@@ -1,561 +0,0 @@
-import React, { useContext, useState, useEffect } from "react";
-import styled from "styled-components";
-
-import { Context } from "shared/Context";
-import api from "shared/api";
-
-import Loading from "components/Loading";
-import TitleSection from "components/TitleSection";
-
-import PorterFormWrapper from "components/porter-form/PorterFormWrapper";
-import Placeholder from "components/OldPlaceholder";
-import AWSCredentialsList from "./credentials/AWSCredentialList";
-import Heading from "components/form-components/Heading";
-import GCPCredentialsList from "./credentials/GCPCredentialList";
-import { getQueryParam, useRouting } from "shared/routing";
-import {
-  InfraTemplateMeta,
-  InfraTemplate,
-  InfraCredentials,
-  ClusterType,
-} from "shared/types";
-import Description from "components/Description";
-import Select from "components/porter-form/field-components/Select";
-import ClusterList from "./credentials/ClusterList";
-import { useLocation, useParams } from "react-router";
-import qs from "qs";
-import AzureCredentialsList from "./credentials/AzureCredentialList";
-
-type Props = {};
-
-type ProvisionParams = {
-  name: string;
-};
-
-type ProvisionQueryParams = {
-  version?: string;
-};
-
-const ProvisionInfra: React.FunctionComponent<Props> = () => {
-  const { name } = useParams<ProvisionParams>();
-  const location = useLocation<ProvisionQueryParams>();
-  const version = getQueryParam({ location }, "version");
-  const origin = getQueryParam({ location }, "origin");
-  const { currentProject, setCurrentError } = useContext(Context);
-  const [templates, setTemplates] = useState<InfraTemplateMeta[]>([]);
-  const [currentTemplate, setCurrentTemplate] = useState<InfraTemplate>(null);
-  const [selectedClusterID, setSelectedClusterID] = useState<number>(null);
-  const [currentCredential, setCurrentCredential] = useState<InfraCredentials>(
-    null
-  );
-
-  const [isLoading, setIsLoading] = useState(false);
-  const [hasError, setHasError] = useState(false);
-
-  const { pushFiltered } = useRouting();
-
-  useEffect(() => {
-    if (currentProject && !name) {
-      api
-        .listInfraTemplates(
-          "<token>",
-          {},
-          {
-            project_id: currentProject.id,
-          }
-        )
-        .then(({ data }) => {
-          if (!Array.isArray(data)) {
-            throw Error("Data is not an array");
-          }
-
-          let templates = data.sort((a, b) =>
-            a.name > b.name ? 1 : b.name > a.name ? -1 : 0
-          );
-
-          setTemplates(templates);
-          setIsLoading(false);
-        })
-        .catch((err) => {
-          console.error(err);
-          setHasError(true);
-          setCurrentError(err.response?.data?.error);
-          setIsLoading(false);
-        });
-    }
-  }, [currentProject, name]);
-
-  useEffect(() => {
-    if (currentProject && name) {
-      let templateVersion = version || "latest";
-
-      setIsLoading(true);
-
-      api
-        .getInfraTemplate(
-          "<token>",
-          {},
-          {
-            project_id: currentProject.id,
-            version: templateVersion,
-            name: name,
-          }
-        )
-        .then(({ data }) => {
-          setCurrentTemplate(data);
-          setIsLoading(false);
-        })
-        .catch((err) => {
-          console.error(err);
-          setHasError(true);
-          setCurrentError(err.response?.data?.error);
-          setIsLoading(false);
-        });
-    } else if (!name) {
-      setCurrentTemplate(null);
-    }
-  }, [currentProject, name, version]);
-
-  const onSubmit = (values: any) => {
-    setIsLoading(true);
-
-    api
-      .provisionInfra(
-        "<token>",
-        {
-          kind: currentTemplate.kind,
-          values: values,
-          aws_integration_id: currentCredential["aws_integration_id"],
-          do_integration_id: currentCredential["do_integration_id"],
-          gcp_integration_id: currentCredential["gcp_integration_id"],
-          azure_integration_id: currentCredential["azure_integration_id"],
-          cluster_id: selectedClusterID || null,
-        },
-        {
-          project_id: currentProject.id,
-        }
-      )
-      .then(({ data }) => {
-        setIsLoading(false);
-
-        if (origin) {
-          pushFiltered(origin, ["project_id"]);
-        } else if (data?.infra_id) {
-          pushFiltered(`/infrastructure/${data?.infra_id}`, ["project_id"]);
-        } else {
-          pushFiltered(`/infrastructure`, ["project_id"]);
-        }
-      })
-      .catch((err) => {
-        console.error(err);
-        setIsLoading(false);
-      });
-  };
-
-  if (hasError) {
-    return <Placeholder>Error</Placeholder>;
-  }
-
-  if (isLoading) {
-    return (
-      <Placeholder>
-        <Loading />
-      </Placeholder>
-    );
-  }
-
-  const renderIcon = (icon: string) => {
-    if (icon) {
-      return <Icon src={icon} />;
-    }
-
-    return (
-      <Polymer>
-        <i className="material-icons">layers</i>
-      </Polymer>
-    );
-  };
-
-  const renderTemplates = () => {
-    return templates.map((template) => {
-      let { name, icon, description } = template;
-
-      return (
-        <TemplateBlock
-          key={name}
-          onClick={() =>
-            pushFiltered(
-              `/infrastructure/provision/${template.name}`,
-              ["project_id"],
-              {
-                version: template.version,
-              }
-            )
-          }
-        >
-          {renderIcon(icon)}
-          <TemplateTitle>{name}</TemplateTitle>
-          <TemplateDescription>{description}</TemplateDescription>
-        </TemplateBlock>
-      );
-    });
-  };
-
-  const renderStepContents = () => {
-    const numSteps = 2 + currentTemplate?.form?.isClusterScoped;
-
-    //   // if credentials need to be set and the list doesn't contain the necessary creds,
-    //   // render a credentials form
-    if (
-      currentTemplate.required_credential != "" &&
-      currentCredential == null
-    ) {
-      if (currentTemplate.required_credential == "aws_integration_id") {
-        return (
-          <ActionContainer>
-            <Heading>Step 1 of {numSteps} - Link AWS Credentials</Heading>
-            <AWSCredentialsList
-              selectCredential={(i) =>
-                setCurrentCredential({
-                  aws_integration_id: i,
-                })
-              }
-            />
-          </ActionContainer>
-        );
-      } else if (currentTemplate.required_credential == "gcp_integration_id") {
-        return (
-          <ActionContainer>
-            <Heading>Step 1 of {numSteps} - Link GCP Credentials</Heading>
-            <GCPCredentialsList
-              selectCredential={(i) =>
-                setCurrentCredential({
-                  gcp_integration_id: i,
-                })
-              }
-            />
-          </ActionContainer>
-        );
-      } else if (
-        currentTemplate.required_credential == "azure_integration_id"
-      ) {
-        return (
-          <ActionContainer>
-            <Heading>Step 1 of {numSteps} - Link Azure Credentials</Heading>
-            <AzureCredentialsList
-              selectCredential={(i) =>
-                setCurrentCredential({
-                  azure_integration_id: i,
-                })
-              }
-            />
-          </ActionContainer>
-        );
-      }
-    }
-
-    if (currentTemplate?.form?.isClusterScoped && !selectedClusterID) {
-      return (
-        <ActionContainer>
-          <Heading>Step 2 of {numSteps} - Select a Cluster</Heading>
-          <ClusterList
-            selectCluster={(cluster_id) => {
-              setSelectedClusterID(cluster_id);
-            }}
-          />
-        </ActionContainer>
-      );
-    }
-
-    return (
-      <ActionContainer>
-        <Heading>
-          Step {numSteps} of {numSteps} - Configure Settings
-        </Heading>
-        <FormContainer>
-          <PorterFormWrapper
-            showStateDebugger={false}
-            formData={currentTemplate.form}
-            valuesToOverride={{}}
-            isReadOnly={false}
-            onSubmit={onSubmit}
-            isInModal={false}
-            hideBottomSpacer={false}
-            saveButtonText={"Provision"}
-          />
-        </FormContainer>
-      </ActionContainer>
-    );
-  };
-
-  const renderTitleSection = () => {
-    if (currentTemplate) {
-      return (
-        <>
-          <TitleSection>{`Provision ${currentTemplate.name}`}</TitleSection>
-          <InfoSection>
-            <Description>
-              {`Input the required configuration settings.`}
-            </Description>
-          </InfoSection>
-          <LineBreak />
-        </>
-      );
-    }
-
-    return (
-      <>
-        <TitleSection>Provision Infrastructure</TitleSection>
-        <InfoSection>
-          <Description>
-            Select the infrastructure template you would like to use for
-            provisioning.
-          </Description>
-        </InfoSection>
-        <LineBreak />
-      </>
-    );
-  };
-
-  const renderContents = () => {
-    if (currentTemplate) {
-      let { name, icon, description } = currentTemplate;
-      return (
-        <ExpandedContainer>
-          <BackArrowContainer>
-            <BackArrow
-              onClick={() =>
-                pushFiltered(origin || `/infrastructure/provision`, [
-                  "project_id",
-                ])
-              }
-            >
-              <i className="material-icons next-icon">navigate_before</i>
-              {origin ? "Back" : "All Templates"}
-            </BackArrow>
-          </BackArrowContainer>
-          <StepContainer>
-            <TemplateMetadataContainer>
-              {renderIcon(icon)}
-              <TemplateTitle>{name}</TemplateTitle>
-              <TemplateDescription>{description}</TemplateDescription>
-            </TemplateMetadataContainer>
-            {renderStepContents()}
-          </StepContainer>
-        </ExpandedContainer>
-      );
-    }
-
-    return <TemplateList>{renderTemplates()}</TemplateList>;
-  };
-
-  return (
-    <TemplatesWrapper>
-      {renderTitleSection()}
-      {renderContents()}
-    </TemplatesWrapper>
-  );
-};
-
-export default ProvisionInfra;
-
-const LineBreak = styled.div`
-  width: calc(100% - 0px);
-  height: 1px;
-  background: #494b4f;
-  margin: 10px 0px 35px;
-`;
-
-const Icon = styled.img`
-  height: 42px;
-  margin-top: 35px;
-  margin-bottom: 13px;
-`;
-
-const Polymer = styled.div`
-  > i {
-    font-size: 34px;
-    margin-top: 38px;
-    margin-bottom: 20px;
-  }
-`;
-
-const TemplateDescription = styled.div`
-  margin-bottom: 26px;
-  color: #ffffff66;
-  text-align: center;
-  font-weight: default;
-  padding: 0px 25px;
-  height: 2.4em;
-  font-size: 12px;
-  display: -webkit-box;
-  overflow: hidden;
-  -webkit-line-clamp: 2;
-  -webkit-box-orient: vertical;
-`;
-
-const TemplateTitle = styled.div`
-  margin-bottom: 12px;
-  width: 80%;
-  text-align: center;
-  font-size: 14px;
-  white-space: nowrap;
-  overflow: hidden;
-  text-overflow: ellipsis;
-`;
-
-const TemplateBlock = styled.div`
-  border: 1px solid #ffffff00;
-  align-items: center;
-  user-select: none;
-  border-radius: 8px;
-  display: flex;
-  font-size: 13px;
-  font-weight: 500;
-  padding: 3px 0px 5px;
-  flex-direction: column;
-  align-item: center;
-  justify-content: space-between;
-  height: 200px;
-  cursor: pointer;
-  color: #ffffff;
-  position: relative;
-  background: #26282f;
-  box-shadow: 0 4px 15px 0px #00000044;
-  :hover {
-    background: #ffffff11;
-  }
-
-  animation: fadeIn 0.3s 0s;
-  @keyframes fadeIn {
-    from {
-      opacity: 0;
-    }
-    to {
-      opacity: 1;
-    }
-  }
-`;
-
-const TemplateList = styled.div`
-  overflow: visible;
-  margin-top: 35px;
-  padding-bottom: 150px;
-  display: grid;
-  grid-column-gap: 25px;
-  grid-row-gap: 25px;
-  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
-`;
-
-const TemplatesWrapper = styled.div`
-  position: relative;
-  min-width: 300px;
-  margin: 0 auto;
-`;
-
-const StyledTitleSection = styled(TitleSection)`
-  display: flex;
-  align-items: center;
-  width: 50%;
-`;
-
-const StepContainer = styled.div`
-  display: flex;
-  justify-content: space-between;
-`;
-
-const TemplateMetadataContainer = styled.div`
-  min-width: 300px;
-  width: 27%;
-  height: 200px;
-  border: 1px solid #ffffff00;
-  align-items: center;
-  user-select: none;
-  border-radius: 8px;
-  display: flex;
-  font-size: 13px;
-  font-weight: 500;
-  padding: 3px 0px 5px;
-  flex-direction: column;
-  align-item: center;
-  justify-content: space-between;
-  color: #ffffff;
-  position: relative;
-  background: #26282f;
-  box-shadow: 0 4px 15px 0px #00000044;
-  animation: fadeIn 0.3s 0s;
-  @keyframes fadeIn {
-    from {
-      opacity: 0;
-    }
-    to {
-      opacity: 1;
-    }
-  }
-`;
-
-const ActionContainer = styled.div`
-  min-width: 500px;
-  width: 70%;
-  min-height: 600px;
-  border: 1px solid #ffffff00;
-  align-items: center;
-  user-select: none;
-  border-radius: 8px;
-  font-size: 13px;
-  font-weight: 500;
-  padding: 3px 0px 5px;
-  color: #ffffff;
-  position: relative;
-  background: #2e3135;
-  margin-left: 20px;
-  padding: 0 40px;
-
-  animation: fadeIn 0.3s 0s;
-  @keyframes fadeIn {
-    from {
-      opacity: 0;
-    }
-    to {
-      opacity: 1;
-    }
-  }
-`;
-
-const BackArrowContainer = styled.div`
-  width: 100%;
-  height: 24px;
-`;
-
-const BackArrow = styled.div`
-  > i {
-    color: #aaaabb;
-    font-size: 18px;
-    margin-right: 6px;
-  }
-
-  color: #aaaabb;
-  display: flex;
-  align-items: center;
-  font-size: 14px;
-  cursor: pointer;
-  width: 120px;
-`;
-
-const ExpandedContainer = styled.div`
-  display: grid;
-  grid-row-gap: 15px;
-  grid-template-columns: 1;
-`;
-
-const FormContainer = styled.div`
-  position: relative;
-  margin: 20px 0;
-`;
-
-const InfoSection = styled.div`
-  margin-top: 36px;
-  font-family: "Work Sans", sans-serif;
-  margin-left: 0px;
-  margin-bottom: 35px;
-`;

+ 0 - 167
dashboard/src/main/home/infrastructure/components/credentials/AWSCredentialForm.tsx

@@ -1,167 +0,0 @@
-import React, { useContext, useEffect, useState } from "react";
-import InputRow from "components/form-components/InputRow";
-import SelectRow from "components/form-components/SelectRow";
-import SaveButton from "components/SaveButton";
-
-import { Context } from "shared/Context";
-import api from "shared/api";
-import styled from "styled-components";
-import Loading from "components/Loading";
-import { Operation, OperationStatus, OperationType } from "shared/types";
-import { readableDate } from "shared/string_utils";
-import Placeholder from "components/OldPlaceholder";
-
-type Props = {
-  setCreatedCredential: (aws_integration_id: number) => void;
-  cancel: () => void;
-};
-
-const regionOptions = [
-  { value: "us-east-1", label: "US East (N. Virginia) us-east-1" },
-  { value: "us-east-2", label: "US East (Ohio) us-east-2" },
-  { value: "us-west-1", label: "US West (N. California) us-west-1" },
-  { value: "us-west-2", label: "US West (Oregon) us-west-2" },
-  { value: "af-south-1", label: "Africa (Cape Town) af-south-1" },
-  { value: "ap-east-1", label: "Asia Pacific (Hong Kong) ap-east-1" },
-  { value: "ap-south-1", label: "Asia Pacific (Mumbai) ap-south-1" },
-  { value: "ap-northeast-2", label: "Asia Pacific (Seoul) ap-northeast-2" },
-  { value: "ap-southeast-1", label: "Asia Pacific (Singapore) ap-southeast-1" },
-  { value: "ap-southeast-2", label: "Asia Pacific (Sydney) ap-southeast-2" },
-  { value: "ap-northeast-1", label: "Asia Pacific (Tokyo) ap-northeast-1" },
-  { value: "ca-central-1", label: "Canada (Central) ca-central-1" },
-  { value: "eu-central-1", label: "Europe (Frankfurt) eu-central-1" },
-  { value: "eu-west-1", label: "Europe (Ireland) eu-west-1" },
-  { value: "eu-west-2", label: "Europe (London) eu-west-2" },
-  { value: "eu-south-1", label: "Europe (Milan) eu-south-1" },
-  { value: "eu-west-3", label: "Europe (Paris) eu-west-3" },
-  { value: "eu-north-1", label: "Europe (Stockholm) eu-north-1" },
-  { value: "me-south-1", label: "Middle East (Bahrain) me-south-1" },
-  { value: "sa-east-1", label: "South America (São Paulo) sa-east-1" },
-];
-
-const AWSCredentialForm: React.FunctionComponent<Props> = ({
-  setCreatedCredential,
-}) => {
-  const { currentProject, setCurrentError } = useContext(Context);
-  const [accessId, setAccessId] = useState("");
-  const [secretKey, setSecretKey] = useState("");
-  const [assumeRoleArn, setAssumeRoleArn] = useState("");
-  const [buttonStatus, setButtonStatus] = useState("");
-  const [awsRegion, setAWSRegion] = useState("us-east-1");
-  const [isLoading, setIsLoading] = useState(false);
-  const [hasError, setHasError] = useState(false);
-
-  const submit = () => {
-    setIsLoading(true);
-
-    api
-      .createAWSIntegration(
-        "<token>",
-        {
-          aws_region: awsRegion,
-          aws_access_key_id: accessId,
-          aws_secret_access_key: secretKey,
-          aws_assume_role_arn: assumeRoleArn,
-        },
-        {
-          id: currentProject.id,
-        }
-      )
-      .then(({ data }) => {
-        setCreatedCredential(data.id);
-        setIsLoading(false);
-      })
-      .catch((err) => {
-        console.error(err);
-        setHasError(true);
-        setCurrentError(err.response?.data?.error);
-        setIsLoading(false);
-      });
-  };
-
-  if (hasError) {
-    return <Placeholder>Error</Placeholder>;
-  }
-
-  if (isLoading) {
-    return (
-      <Placeholder>
-        <Loading />
-      </Placeholder>
-    );
-  }
-
-  return (
-    <>
-      <InputRow
-        type="text"
-        value={accessId}
-        setValue={(x: string) => {
-          setAccessId(x);
-        }}
-        label="👤 AWS Access ID"
-        placeholder="ex: AKIAIOSFODNN7EXAMPLE"
-        width="100%"
-        isRequired={true}
-      />
-      <InputRow
-        type="password"
-        value={secretKey}
-        setValue={(x: string) => {
-          setSecretKey(x);
-        }}
-        label="🔒 AWS Secret Key"
-        placeholder="○ ○ ○ ○ ○ ○ ○ ○ ○"
-        width="100%"
-        isRequired={true}
-      />
-      <SelectRow
-        options={regionOptions}
-        width="100%"
-        scrollBuffer={true}
-        value={awsRegion}
-        dropdownMaxHeight="240px"
-        setActiveValue={(x: string) => {
-          setAWSRegion(x);
-        }}
-        label="📍 AWS Region"
-      />
-      <InputRow
-        type="text"
-        value={assumeRoleArn}
-        setValue={(x: string) => {
-          setAssumeRoleArn(x);
-        }}
-        label="👤 (Optional) AWS Assume Role ARN"
-        placeholder="ex: arn:aws:iam::01234567890:role/my_assumed_role"
-        width="100%"
-        isRequired={false}
-      />
-      <Flex>
-        <SaveButton
-          text="Continue"
-          disabled={false}
-          onClick={submit}
-          makeFlush={true}
-          clearPosition={true}
-          status={buttonStatus}
-          statusPosition={"right"}
-        />
-      </Flex>
-    </>
-  );
-};
-
-export default AWSCredentialForm;
-
-const Flex = styled.div`
-  display: flex;
-  color: #ffffff;
-  align-items: center;
-  margin-top: 30px;
-  > i {
-    color: #aaaabb;
-    font-size: 20px;
-    margin-right: 10px;
-  }
-`;

+ 0 - 108
dashboard/src/main/home/infrastructure/components/credentials/AWSCredentialList.tsx

@@ -1,108 +0,0 @@
-import React, { useContext, useEffect, useState } from "react";
-import { Context } from "shared/Context";
-import api from "shared/api";
-import styled from "styled-components";
-import Loading from "components/Loading";
-import Placeholder from "components/OldPlaceholder";
-import AWSCredentialForm from "./AWSCredentialForm";
-import CredentialList from "./CredentialList";
-import Description from "components/Description";
-
-type Props = {
-  selectCredential: (aws_integration_id: number) => void;
-};
-
-type AWSCredential = {
-  created_at: string;
-  id: number;
-  user_id: number;
-  project_id: number;
-  aws_arn: string;
-};
-
-const AWSCredentialsList: React.FunctionComponent<Props> = ({
-  selectCredential,
-}) => {
-  const { currentProject, setCurrentError } = useContext(Context);
-  const [isLoading, setIsLoading] = useState(true);
-  const [awsCredentials, setAWSCredentials] = useState<AWSCredential[]>(null);
-  const [shouldCreateCred, setShouldCreateCred] = useState(false);
-  const [hasError, setHasError] = useState(false);
-
-  useEffect(() => {
-    api
-      .getAWSIntegration(
-        "<token>",
-        {},
-        {
-          project_id: currentProject.id,
-        }
-      )
-      .then(({ data }) => {
-        if (!Array.isArray(data)) {
-          throw Error("Data is not an array");
-        }
-
-        setAWSCredentials(data);
-        setIsLoading(false);
-      })
-      .catch((err) => {
-        console.error(err);
-        setHasError(true);
-        setCurrentError(err.response?.data?.error);
-        setIsLoading(false);
-      });
-  }, [currentProject]);
-
-  if (hasError) {
-    return <Placeholder>Error</Placeholder>;
-  }
-
-  if (isLoading) {
-    return (
-      <Placeholder>
-        <Loading />
-      </Placeholder>
-    );
-  }
-
-  const renderContents = () => {
-    if (shouldCreateCred) {
-      return (
-        <AWSCredentialForm
-          setCreatedCredential={selectCredential}
-          cancel={() => {}}
-        />
-      );
-    }
-
-    return (
-      <>
-        <Description>
-          Select your credentials from the list below, or create a new
-          credential:
-        </Description>
-        <CredentialList
-          credentials={awsCredentials.map((cred) => {
-            return {
-              id: cred.id,
-              display_name: cred.aws_arn,
-              created_at: cred.created_at,
-            };
-          })}
-          selectCredential={selectCredential}
-          shouldCreateCred={() => setShouldCreateCred(true)}
-          addNewText="Add New AWS Credential"
-        />
-      </>
-    );
-  };
-
-  return <AWSCredentialWrapper>{renderContents()}</AWSCredentialWrapper>;
-};
-
-export default AWSCredentialsList;
-
-const AWSCredentialWrapper = styled.div`
-  margin-top: 20px;
-`;

+ 0 - 140
dashboard/src/main/home/infrastructure/components/credentials/AzureCredentialForm.tsx

@@ -1,140 +0,0 @@
-import React, { useContext, useState } from "react";
-import InputRow from "components/form-components/InputRow";
-import SaveButton from "components/SaveButton";
-
-import { Context } from "shared/Context";
-import api from "shared/api";
-import styled from "styled-components";
-import Loading from "components/Loading";
-import Placeholder from "components/OldPlaceholder";
-
-type Props = {
-  setCreatedCredential: (aws_integration_id: number) => void;
-  cancel: () => void;
-};
-
-const AzureCredentialForm: React.FunctionComponent<Props> = ({
-  setCreatedCredential,
-}) => {
-  const { currentProject, setCurrentError } = useContext(Context);
-  const [clientId, setClientId] = useState("");
-  const [servicePrincipalKey, setServicePrincipalKey] = useState("");
-  const [tenantId, setTenantId] = useState("");
-  const [subscriptionId, setSubscriptionId] = useState("");
-  const [buttonStatus, setButtonStatus] = useState("");
-  const [isLoading, setIsLoading] = useState(false);
-  const [hasError, setHasError] = useState(false);
-
-  const submit = () => {
-    setIsLoading(true);
-
-    api
-      .createAzureIntegration(
-        "<token>",
-        {
-          azure_client_id: clientId,
-          azure_subscription_id: subscriptionId,
-          azure_tenant_id: tenantId,
-          service_principal_key: servicePrincipalKey,
-        },
-        {
-          id: currentProject.id,
-        }
-      )
-      .then(({ data }) => {
-        setCreatedCredential(data.id);
-        setIsLoading(false);
-      })
-      .catch((err) => {
-        console.error(err);
-        setHasError(true);
-        setCurrentError(err.response?.data?.error);
-        setIsLoading(false);
-      });
-  };
-
-  if (hasError) {
-    return <Placeholder>Error</Placeholder>;
-  }
-
-  if (isLoading) {
-    return (
-      <Placeholder>
-        <Loading />
-      </Placeholder>
-    );
-  }
-
-  return (
-    <>
-      <InputRow
-        type="text"
-        value={clientId}
-        setValue={(x: string) => {
-          setClientId(x);
-        }}
-        label="👤 Azure Client ID"
-        placeholder="ex. 12345678-abcd-1234-abcd-12345678abcd"
-        width="100%"
-        isRequired={true}
-      />
-      <InputRow
-        type="password"
-        value={servicePrincipalKey}
-        setValue={(x: string) => {
-          setServicePrincipalKey(x);
-        }}
-        label="🔒 Azure Service Principal Key"
-        placeholder="○ ○ ○ ○ ○ ○ ○ ○ ○"
-        width="100%"
-        isRequired={true}
-      />
-      <InputRow
-        type="text"
-        value={tenantId}
-        setValue={(x: string) => {
-          setTenantId(x);
-        }}
-        label="Azure Tenant ID"
-        placeholder="ex. 12345678-abcd-1234-abcd-12345678abcd"
-        width="100%"
-        isRequired={true}
-      />
-      <InputRow
-        type="text"
-        value={subscriptionId}
-        setValue={(x: string) => {
-          setSubscriptionId(x);
-        }}
-        label="Azure Subscription ID"
-        placeholder="ex. 12345678-abcd-1234-abcd-12345678abcd"
-        width="100%"
-        isRequired={true}
-      />
-      <Flex>
-        <SaveButton
-          text="Continue"
-          disabled={false}
-          onClick={submit}
-          makeFlush={true}
-          clearPosition={true}
-          status={buttonStatus}
-          statusPosition={"right"}
-        />
-      </Flex>
-    </>
-  );
-};
-
-export default AzureCredentialForm;
-
-const Flex = styled.div`
-  display: flex;
-  color: #ffffff;
-  align-items: center;
-  > i {
-    color: #aaaabb;
-    font-size: 20px;
-    margin-right: 10px;
-  }
-`;

+ 0 - 110
dashboard/src/main/home/infrastructure/components/credentials/AzureCredentialList.tsx

@@ -1,110 +0,0 @@
-import React, { useContext, useEffect, useState } from "react";
-import { Context } from "shared/Context";
-import api from "shared/api";
-import styled from "styled-components";
-import Loading from "components/Loading";
-import Placeholder from "components/OldPlaceholder";
-import AzureCredentialForm from "./AzureCredentialForm";
-import CredentialList from "./CredentialList";
-import Description from "components/Description";
-
-type Props = {
-  selectCredential: (azure_integration_id: number) => void;
-};
-
-type AzureCredential = {
-  created_at: string;
-  id: number;
-  user_id: number;
-  project_id: number;
-  azure_client_id: string;
-};
-
-const AzureCredentialsList: React.FunctionComponent<Props> = ({
-  selectCredential,
-}) => {
-  const { currentProject, setCurrentError } = useContext(Context);
-  const [isLoading, setIsLoading] = useState(true);
-  const [azCredentials, setAzureCredentials] = useState<AzureCredential[]>(
-    null
-  );
-  const [shouldCreateCred, setShouldCreateCred] = useState(false);
-  const [hasError, setHasError] = useState(false);
-
-  useEffect(() => {
-    api
-      .getAzureIntegration(
-        "<token>",
-        {},
-        {
-          project_id: currentProject.id,
-        }
-      )
-      .then(({ data }) => {
-        if (!Array.isArray(data)) {
-          throw Error("Data is not an array");
-        }
-
-        setAzureCredentials(data);
-        setIsLoading(false);
-      })
-      .catch((err) => {
-        console.error(err);
-        setHasError(true);
-        setCurrentError(err.response?.data?.error);
-        setIsLoading(false);
-      });
-  }, [currentProject]);
-
-  if (hasError) {
-    return <Placeholder>Error</Placeholder>;
-  }
-
-  if (isLoading) {
-    return (
-      <Placeholder>
-        <Loading />
-      </Placeholder>
-    );
-  }
-
-  const renderContents = () => {
-    if (shouldCreateCred) {
-      return (
-        <AzureCredentialForm
-          setCreatedCredential={selectCredential}
-          cancel={() => {}}
-        />
-      );
-    }
-
-    return (
-      <>
-        <Description>
-          Select your credentials from the list below, or create a new
-          credential:
-        </Description>
-        <CredentialList
-          credentials={azCredentials.map((cred) => {
-            return {
-              id: cred.id,
-              display_name: cred.azure_client_id,
-              created_at: cred.created_at,
-            };
-          })}
-          selectCredential={selectCredential}
-          shouldCreateCred={() => setShouldCreateCred(true)}
-          addNewText="Add New Azure Credential"
-        />
-      </>
-    );
-  };
-
-  return <AzureCredentialWrapper>{renderContents()}</AzureCredentialWrapper>;
-};
-
-export default AzureCredentialsList;
-
-const AzureCredentialWrapper = styled.div`
-  margin-top: 20px;
-`;

+ 0 - 101
dashboard/src/main/home/infrastructure/components/credentials/ClusterList.tsx

@@ -1,101 +0,0 @@
-import React, { useContext, useEffect, useState } from "react";
-import { Context } from "shared/Context";
-import api from "shared/api";
-import Loading from "components/Loading";
-import Placeholder from "components/OldPlaceholder";
-import Description from "components/Description";
-import { ClusterType } from "shared/types";
-import SelectRow from "components/form-components/SelectRow";
-import SaveButton from "components/SaveButton";
-
-type Props = {
-  selectCluster: (cluster_id: number) => void;
-};
-
-const ClusterList: React.FunctionComponent<Props> = ({ selectCluster }) => {
-  const { currentProject, setCurrentError } = useContext(Context);
-  const [isLoading, setIsLoading] = useState(true);
-  const [clusters, setClusters] = useState<ClusterType[]>([]);
-  const [selectedClusterID, setSelectedClusterID] = useState<number>();
-  const [hasError, setHasError] = useState(false);
-
-  useEffect(() => {
-    api
-      .getClusters(
-        "<token>",
-        {},
-        {
-          id: currentProject.id,
-        }
-      )
-      .then(({ data }) => {
-        if (!Array.isArray(data)) {
-          throw Error("Data is not an array");
-        }
-
-        setClusters(data);
-        setSelectedClusterID(data[0]?.id);
-        setIsLoading(false);
-      })
-      .catch((err) => {
-        console.error(err);
-        setHasError(true);
-        setCurrentError(err.response?.data?.error);
-        setIsLoading(false);
-      });
-  }, [currentProject]);
-
-  if (hasError) {
-    return <Placeholder>Error</Placeholder>;
-  }
-
-  if (isLoading || !clusters) {
-    return (
-      <Placeholder>
-        <Loading />
-      </Placeholder>
-    );
-  }
-
-  if (clusters.length == 0) {
-    return (
-      <Placeholder>
-        At least one cluster must exist to create this resource
-      </Placeholder>
-    );
-  }
-
-  return (
-    <>
-      <Description>
-        Select your credentials from the list below, or create a new credential:
-      </Description>
-      <SelectRow
-        options={clusters.map((cluster, i) => {
-          return {
-            label: cluster.name,
-            value: "" + cluster.id,
-          };
-        })}
-        width="100%"
-        scrollBuffer={true}
-        value={"" + selectedClusterID}
-        dropdownMaxHeight="240px"
-        setActiveValue={(x: string) => {
-          setSelectedClusterID(parseInt(x));
-        }}
-        label="Cluster Options"
-      />
-      <SaveButton
-        text="Continue"
-        disabled={false}
-        onClick={() => selectCluster(selectedClusterID)}
-        makeFlush={true}
-        clearPosition={true}
-        statusPosition={"right"}
-      />
-    </>
-  );
-};
-
-export default ClusterList;

+ 0 - 115
dashboard/src/main/home/infrastructure/components/credentials/CredentialList.tsx

@@ -1,115 +0,0 @@
-import React from "react";
-import styled from "styled-components";
-import { readableDate } from "shared/string_utils";
-
-type Props = {
-  selectCredential: (id: number) => void;
-  credentials: GenericCredential[];
-  addNewText: string;
-  shouldCreateCred: () => void;
-  isLink?: boolean;
-  linkHref?: string;
-};
-
-type GenericCredential = {
-  id: number;
-  display_name: string;
-  created_at: string;
-};
-
-const CredentialList: React.FunctionComponent<Props> = (props) => {
-  const renderCreateSection = () => {
-    let inner = (
-      <Flex>
-        <i className="material-icons">account_circle</i>
-        {props.addNewText}
-      </Flex>
-    );
-
-    if (props.isLink) {
-      return <CreateNewRowLink href={props.linkHref}>{inner}</CreateNewRowLink>;
-    }
-
-    return (
-      <CreateNewRow onClick={props.shouldCreateCred}>{inner}</CreateNewRow>
-    );
-  };
-
-  return (
-    <>
-      {props.credentials.map((cred) => {
-        return (
-          <PreviewRow
-            key={cred.id}
-            onClick={() => props.selectCredential(cred.id)}
-          >
-            <Flex>
-              <i className="material-icons">account_circle</i>
-              {cred.display_name || "Name N/A"}
-            </Flex>
-            <Right>Connected at {readableDate(cred.created_at)}</Right>
-          </PreviewRow>
-        );
-      })}
-      {renderCreateSection()}
-    </>
-  );
-};
-
-export default CredentialList;
-
-const PreviewRow = styled.div`
-  display: flex;
-  align-items: center;
-  padding: 12px 15px;
-  color: #ffffff55;
-  background: #ffffff01;
-  border: 1px solid #aaaabb;
-  justify-content: space-between;
-  font-size: 13px;
-  border-radius: 5px;
-  cursor: pointer;
-  margin: 16px 0;
-
-  :hover {
-    background: #ffffff10;
-  }
-`;
-
-const Flex = styled.div`
-  display: flex;
-  color: #ffffff;
-  align-items: center;
-  > i {
-    color: #aaaabb;
-    font-size: 20px;
-    margin-right: 10px;
-  }
-`;
-
-const Right = styled.div`
-  text-align: right;
-`;
-
-const CreateNewRow = styled(PreviewRow)`
-  background: none;
-`;
-
-const CreateNewRowLink = styled.a`
-  background: none;
-  display: flex;
-  align-items: center;
-  padding: 12px 15px;
-  color: #ffffff55;
-  background: #ffffff01;
-  border: 1px solid #aaaabb;
-  justify-content: space-between;
-  font-size: 13px;
-  border-radius: 5px;
-  cursor: pointer;
-  margin: 16px 0;
-
-  :hover {
-    background: #ffffff10;
-  }
-`;

+ 0 - 114
dashboard/src/main/home/infrastructure/components/credentials/GCPCredentialForm.tsx

@@ -1,114 +0,0 @@
-import React, { useContext, useState } from "react";
-import InputRow from "components/form-components/InputRow";
-import SaveButton from "components/SaveButton";
-
-import { Context } from "shared/Context";
-import api from "shared/api";
-import styled from "styled-components";
-import Loading from "components/Loading";
-import Placeholder from "components/OldPlaceholder";
-import Helper from "components/form-components/Helper";
-import UploadArea from "components/form-components/UploadArea";
-
-type Props = {
-  setCreatedCredential: (aws_integration_id: number) => void;
-  cancel: () => void;
-};
-
-const GCPCredentialForm: React.FunctionComponent<Props> = ({
-  setCreatedCredential,
-}) => {
-  const { currentProject, setCurrentError } = useContext(Context);
-  const [buttonStatus, setButtonStatus] = useState("");
-  const [projectId, setProjectId] = useState("");
-  const [serviceAccountKey, setServiceAccountKey] = useState("");
-  const [isLoading, setIsLoading] = useState(false);
-  const [hasError, setHasError] = useState(false);
-
-  const submit = () => {
-    setIsLoading(true);
-    api
-      .createGCPIntegration(
-        "<token>",
-        {
-          gcp_key_data: serviceAccountKey,
-          gcp_project_id: projectId,
-        },
-        {
-          project_id: currentProject.id,
-        }
-      )
-      .then(({ data }) => {
-        setCreatedCredential(data.id);
-        setIsLoading(false);
-      })
-      .catch((err) => {
-        console.error(err);
-        setHasError(true);
-        setCurrentError(err.response?.data?.error);
-        setIsLoading(false);
-      });
-  };
-
-  if (hasError) {
-    return <Placeholder>Error</Placeholder>;
-  }
-
-  if (isLoading) {
-    return (
-      <Placeholder>
-        <Loading />
-      </Placeholder>
-    );
-  }
-
-  return (
-    <>
-      <InputRow
-        type="text"
-        value={projectId}
-        setValue={(x: string) => {
-          setProjectId(x);
-        }}
-        label="🏷️ GCP Project ID"
-        placeholder="ex: blindfold-ceiling-24601"
-        width="100%"
-        isRequired={true}
-      />
-
-      <Helper>Service account credentials for GCP permissions.</Helper>
-      <UploadArea
-        setValue={(x: any) => setServiceAccountKey(x)}
-        label="🔒 GCP Key Data (JSON)"
-        placeholder="Choose a file or drag it here."
-        width="100%"
-        height="100%"
-        isRequired={true}
-      />
-      <Flex>
-        <SaveButton
-          text="Continue"
-          disabled={false}
-          onClick={submit}
-          makeFlush={true}
-          clearPosition={true}
-          status={buttonStatus}
-          statusPosition={"right"}
-        />
-      </Flex>
-    </>
-  );
-};
-
-export default GCPCredentialForm;
-
-const Flex = styled.div`
-  display: flex;
-  color: #ffffff;
-  align-items: center;
-  > i {
-    color: #aaaabb;
-    font-size: 20px;
-    margin-right: 10px;
-  }
-`;

+ 0 - 108
dashboard/src/main/home/infrastructure/components/credentials/GCPCredentialList.tsx

@@ -1,108 +0,0 @@
-import React, { useContext, useEffect, useState } from "react";
-import { Context } from "shared/Context";
-import api from "shared/api";
-import styled from "styled-components";
-import Loading from "components/Loading";
-import Placeholder from "components/OldPlaceholder";
-import GCPCredentialForm from "./GCPCredentialForm";
-import CredentialList from "./CredentialList";
-import Description from "components/Description";
-
-type Props = {
-  selectCredential: (gcp_integration_id: number) => void;
-};
-
-type GCPCredential = {
-  created_at: string;
-  id: number;
-  user_id: number;
-  project_id: number;
-  gcp_sa_email: string;
-};
-
-const GCPCredentialsList: React.FunctionComponent<Props> = ({
-  selectCredential,
-}) => {
-  const { currentProject, setCurrentError } = useContext(Context);
-  const [isLoading, setIsLoading] = useState(true);
-  const [gcpCredentials, setGCPCredentials] = useState<GCPCredential[]>(null);
-  const [shouldCreateCred, setShouldCreateCred] = useState(false);
-  const [hasError, setHasError] = useState(false);
-
-  useEffect(() => {
-    api
-      .getGCPIntegration(
-        "<token>",
-        {},
-        {
-          project_id: currentProject.id,
-        }
-      )
-      .then(({ data }) => {
-        if (!Array.isArray(data)) {
-          throw Error("Data is not an array");
-        }
-
-        setGCPCredentials(data);
-        setIsLoading(false);
-      })
-      .catch((err) => {
-        console.error(err);
-        setHasError(true);
-        setCurrentError(err.response?.data?.error);
-        setIsLoading(false);
-      });
-  }, [currentProject]);
-
-  if (hasError) {
-    return <Placeholder>Error</Placeholder>;
-  }
-
-  if (isLoading) {
-    return (
-      <Placeholder>
-        <Loading />
-      </Placeholder>
-    );
-  }
-
-  const renderContents = () => {
-    if (shouldCreateCred) {
-      return (
-        <GCPCredentialForm
-          setCreatedCredential={selectCredential}
-          cancel={() => {}}
-        />
-      );
-    }
-
-    return (
-      <>
-        <Description>
-          Select your credentials from the list below, or create a new
-          credential:
-        </Description>
-        <CredentialList
-          credentials={gcpCredentials.map((cred) => {
-            return {
-              id: cred.id,
-              display_name: cred.gcp_sa_email,
-              created_at: cred.created_at,
-            };
-          })}
-          selectCredential={selectCredential}
-          shouldCreateCred={() => setShouldCreateCred(true)}
-          addNewText="Add New GCP Credential"
-        />
-      </>
-    );
-  };
-
-  return <GCPCredentialWrapper>{renderContents()}</GCPCredentialWrapper>;
-};
-
-export default GCPCredentialsList;
-
-const GCPCredentialWrapper = styled.div`
-  margin-top: 20px;
-`;

+ 0 - 105
dashboard/src/main/home/integrations/edit-integration/DockerHubForm.tsx

@@ -1,105 +0,0 @@
-import React, { Component } from "react";
-import styled from "styled-components";
-
-import InputRow from "components/form-components/InputRow";
-import SaveButton from "components/SaveButton";
-
-type PropsType = {
-  closeForm: () => void;
-};
-
-type StateType = {
-  registryURL: string;
-  dockerEmail: string;
-  dockerUsername: string;
-  dockerPassword: string;
-};
-
-export default class DockerHubForm extends Component<PropsType, StateType> {
-  state = {
-    registryURL: "",
-    dockerEmail: "",
-    dockerUsername: "",
-    dockerPassword: "",
-  };
-
-  isDisabled = (): boolean => {
-    let {
-      registryURL,
-      dockerEmail,
-      dockerUsername,
-      dockerPassword,
-    } = this.state;
-    if (
-      registryURL === "" ||
-      dockerEmail === "" ||
-      dockerUsername === "" ||
-      dockerPassword === ""
-    ) {
-      return true;
-    }
-    return false;
-  };
-
-  handleSubmit = () => {
-    // TODO: implement once api is restructured
-  };
-
-  render() {
-    return (
-      <StyledForm>
-        <CredentialWrapper>
-          <InputRow
-            type="text"
-            value={this.state.registryURL}
-            setValue={(x: string) => this.setState({ registryURL: x })}
-            label="📦 Registry URL"
-            placeholder="ex: index.docker.io"
-            width="100%"
-          />
-          <InputRow
-            type="text"
-            value={this.state.dockerEmail}
-            setValue={(x: string) => this.setState({ dockerEmail: x })}
-            label="✉️ Docker Email"
-            placeholder="ex: captain@ahab.com"
-            width="100%"
-          />
-          <InputRow
-            type="text"
-            value={this.state.dockerUsername}
-            setValue={(x: string) => this.setState({ dockerUsername: x })}
-            label="👤 Docker Username"
-            placeholder="ex: whale_watcher_2000"
-            width="100%"
-          />
-          <InputRow
-            type="password"
-            value={this.state.dockerPassword}
-            setValue={(x: string) => this.setState({ dockerPassword: x })}
-            label="🔒 Docker Password"
-            placeholder="○ ○ ○ ○ ○ ○ ○ ○ ○"
-            width="100%"
-          />
-        </CredentialWrapper>
-        <SaveButton
-          text="Save Settings"
-          makeFlush={true}
-          disabled={this.isDisabled()}
-          onClick={this.isDisabled() ? null : this.handleSubmit}
-        />
-      </StyledForm>
-    );
-  }
-}
-
-const CredentialWrapper = styled.div`
-  padding: 5px 40px 25px;
-  background: #ffffff11;
-  border-radius: 5px;
-`;
-
-const StyledForm = styled.div`
-  position: relative;
-  padding-bottom: 75px;
-`;

+ 0 - 139
dashboard/src/main/home/integrations/edit-integration/ECRForm.tsx

@@ -1,139 +0,0 @@
-import React, { Component } from "react";
-import styled from "styled-components";
-
-import { Context } from "shared/Context";
-import api from "shared/api";
-
-import InputRow from "components/form-components/InputRow";
-import SaveButton from "components/SaveButton";
-import Heading from "components/form-components/Heading";
-import Helper from "components/form-components/Helper";
-
-type PropsType = {
-  closeForm: () => void;
-};
-
-type StateType = {
-  credentialsName: string;
-  awsRegion: string;
-  awsAccessId: string;
-  awsSecretKey: string;
-};
-
-export default class ECRForm extends Component<PropsType, StateType> {
-  state = {
-    credentialsName: "",
-    awsRegion: "",
-    awsAccessId: "",
-    awsSecretKey: "",
-  };
-
-  isDisabled = (): boolean => {
-    let { awsRegion, awsAccessId, awsSecretKey, credentialsName } = this.state;
-    if (
-      awsRegion === "" ||
-      awsAccessId === "" ||
-      awsSecretKey === "" ||
-      credentialsName === ""
-    ) {
-      return true;
-    }
-    return false;
-  };
-
-  catchErr = (err: any) => console.log(err);
-
-  handleSubmit = () => {
-    let { awsRegion, awsAccessId, awsSecretKey, credentialsName } = this.state;
-    let { currentProject } = this.context;
-
-    api
-      .createAWSIntegration(
-        "<token>",
-        {
-          aws_region: awsRegion,
-          aws_access_key_id: awsAccessId,
-          aws_secret_access_key: awsSecretKey,
-        },
-        { id: currentProject.id }
-      )
-      .then((res) =>
-        api.connectECRRegistry(
-          "<token>",
-          {
-            name: credentialsName,
-            aws_integration_id: res.data.id,
-          },
-          { id: currentProject.id }
-        )
-      )
-      .then(() => this.props.closeForm())
-      .catch(this.catchErr);
-  };
-
-  render() {
-    return (
-      <StyledForm>
-        <CredentialWrapper>
-          <Heading>Porter Settings</Heading>
-          <Helper>
-            Give a name to this set of registry credentials (just for Porter).
-          </Helper>
-          <InputRow
-            type="text"
-            value={this.state.credentialsName}
-            setValue={(x: string) => this.setState({ credentialsName: x })}
-            label="🏷️ Registry Name"
-            placeholder="ex: paper-straw"
-            width="100%"
-          />
-          <Heading>AWS Settings</Heading>
-          <Helper>AWS access credentials.</Helper>
-          <InputRow
-            type="text"
-            value={this.state.awsRegion}
-            setValue={(x: string) => this.setState({ awsRegion: x })}
-            label="📍 AWS Region"
-            placeholder="ex: mars-north-12"
-            width="100%"
-          />
-          <InputRow
-            type="text"
-            value={this.state.awsAccessId}
-            setValue={(x: string) => this.setState({ awsAccessId: x })}
-            label="👤 AWS Access ID"
-            placeholder="ex: AKIAIOSFODNN7EXAMPLE"
-            width="100%"
-          />
-          <InputRow
-            type="password"
-            value={this.state.awsSecretKey}
-            setValue={(x: string) => this.setState({ awsSecretKey: x })}
-            label="🔒 AWS Secret Key"
-            placeholder="○ ○ ○ ○ ○ ○ ○ ○ ○"
-            width="100%"
-          />
-        </CredentialWrapper>
-        <SaveButton
-          text="Save Settings"
-          makeFlush={true}
-          disabled={this.isDisabled()}
-          onClick={this.isDisabled() ? null : this.handleSubmit}
-        />
-      </StyledForm>
-    );
-  }
-}
-
-ECRForm.contextType = Context;
-
-const CredentialWrapper = styled.div`
-  padding: 5px 40px 25px;
-  background: #ffffff11;
-  border-radius: 5px;
-`;
-
-const StyledForm = styled.div`
-  position: relative;
-  padding-bottom: 75px;
-`;

+ 0 - 124
dashboard/src/main/home/integrations/edit-integration/EKSForm.tsx

@@ -1,124 +0,0 @@
-import React, { Component } from "react";
-import styled from "styled-components";
-
-import InputRow from "components/form-components/InputRow";
-import TextArea from "components/form-components/TextArea";
-import SaveButton from "components/SaveButton";
-import Heading from "components/form-components/Heading";
-import Helper from "components/form-components/Helper";
-
-type PropsType = {
-  closeForm: () => void;
-};
-
-type StateType = {
-  clusterName: string;
-  clusterEndpoint: string;
-  clusterCA: string;
-  awsAccessId: string;
-  awsSecretKey: string;
-};
-
-export default class EKSForm extends Component<PropsType, StateType> {
-  state = {
-    clusterName: "",
-    clusterEndpoint: "",
-    clusterCA: "",
-    awsAccessId: "",
-    awsSecretKey: "",
-  };
-
-  isDisabled = (): boolean => {
-    let {
-      clusterName,
-      clusterEndpoint,
-      clusterCA,
-      awsAccessId,
-      awsSecretKey,
-    } = this.state;
-    if (
-      clusterName === "" ||
-      clusterEndpoint === "" ||
-      clusterCA === "" ||
-      awsAccessId === "" ||
-      awsSecretKey === ""
-    ) {
-      return true;
-    }
-    return false;
-  };
-
-  handleSubmit = () => {
-    // TODO: implement once api is restructured
-  };
-
-  render() {
-    return (
-      <StyledForm>
-        <CredentialWrapper>
-          <Heading>Cluster Settings</Heading>
-          <Helper>Credentials for accessing your GKE cluster.</Helper>
-          <InputRow
-            type="text"
-            value={this.state.clusterName}
-            setValue={(x: string) => this.setState({ clusterName: x })}
-            label="🏷️ Cluster Name"
-            placeholder="ex: briny-pagelet"
-            width="100%"
-          />
-          <InputRow
-            type="text"
-            value={this.state.clusterEndpoint}
-            setValue={(x: string) => this.setState({ clusterEndpoint: x })}
-            label="🌐 Cluster Endpoint"
-            placeholder="ex: 00.00.000.00"
-            width="100%"
-          />
-          <TextArea
-            value={this.state.clusterCA}
-            setValue={(x: string) => this.setState({ clusterCA: x })}
-            label="🔏 Cluster Certificate"
-            placeholder="(Paste your certificate here)"
-            width="100%"
-          />
-
-          <Heading>AWS Settings</Heading>
-          <Helper>AWS access credentials.</Helper>
-          <InputRow
-            type="text"
-            value={this.state.awsAccessId}
-            setValue={(x: string) => this.setState({ awsAccessId: x })}
-            label="👤 AWS Access ID"
-            placeholder="ex: AKIAIOSFODNN7EXAMPLE"
-            width="100%"
-          />
-          <InputRow
-            type="password"
-            value={this.state.awsSecretKey}
-            setValue={(x: string) => this.setState({ awsSecretKey: x })}
-            label="🔒 AWS Secret Key"
-            placeholder="○ ○ ○ ○ ○ ○ ○ ○ ○"
-            width="100%"
-          />
-        </CredentialWrapper>
-        <SaveButton
-          text="Save Settings"
-          makeFlush={true}
-          disabled={this.isDisabled()}
-          onClick={this.isDisabled() ? null : this.handleSubmit}
-        />
-      </StyledForm>
-    );
-  }
-}
-
-const CredentialWrapper = styled.div`
-  padding: 5px 40px 25px;
-  background: #ffffff11;
-  border-radius: 5px;
-`;
-
-const StyledForm = styled.div`
-  position: relative;
-  padding-bottom: 75px;
-`;

+ 0 - 38
dashboard/src/main/home/integrations/edit-integration/EditIntegrationForm.tsx

@@ -1,38 +0,0 @@
-import React, { Component } from "react";
-
-import DockerHubForm from "./DockerHubForm";
-import GKEForm from "./GKEForm";
-import EKSForm from "./EKSForm";
-import GCRForm from "./GCRForm";
-import ECRForm from "./ECRForm";
-
-type PropsType = {
-  integrationName: string;
-  closeForm: () => void;
-};
-
-type StateType = {};
-
-export default class CreateIntegrationForm extends Component<
-  PropsType,
-  StateType
-> {
-  state = {};
-
-  render = () => {
-    switch (this.props.integrationName) {
-      case "docker-hub":
-        return <DockerHubForm closeForm={this.props.closeForm} />;
-      case "gke":
-        return <GKEForm closeForm={this.props.closeForm} />;
-      case "eks":
-        return <EKSForm closeForm={this.props.closeForm} />;
-      case "ecr":
-        return <ECRForm closeForm={this.props.closeForm} />;
-      case "gcr":
-        return <GCRForm closeForm={this.props.closeForm} />;
-      default:
-        return null;
-    }
-  };
-}

+ 0 - 147
dashboard/src/main/home/integrations/edit-integration/GCRForm.tsx

@@ -1,147 +0,0 @@
-import React, { Component } from "react";
-import styled from "styled-components";
-
-import { Context } from "shared/Context";
-import api from "shared/api";
-
-import InputRow from "components/form-components/InputRow";
-import TextArea from "components/form-components/TextArea";
-import SaveButton from "components/SaveButton";
-import Heading from "components/form-components/Heading";
-import Helper from "components/form-components/Helper";
-
-type PropsType = {
-  closeForm: () => void;
-};
-
-type StateType = {
-  credentialsName: string;
-  serviceAccountKey: string;
-  gcpProjectID: string;
-  url: string;
-};
-
-export default class GCRForm extends Component<PropsType, StateType> {
-  state = {
-    credentialsName: "",
-    serviceAccountKey: "",
-    gcpProjectID: "",
-    url: "",
-  };
-
-  isDisabled = (): boolean => {
-    let { credentialsName, gcpProjectID, serviceAccountKey } = this.state;
-    if (
-      credentialsName === "" ||
-      serviceAccountKey === "" ||
-      gcpProjectID === ""
-    ) {
-      return true;
-    }
-    return false;
-  };
-
-  catchError = (err: any) => console.log(err);
-
-  handleSubmit = () => {
-    let { currentProject } = this.context;
-
-    api
-      .createGCPIntegration(
-        "<token>",
-        {
-          gcp_key_data: this.state.serviceAccountKey,
-          gcp_project_id: this.state.gcpProjectID,
-        },
-        {
-          project_id: currentProject.id,
-        }
-      )
-      .then((res) =>
-        api.connectGCRRegistry(
-          "<token>",
-          {
-            name: this.state.credentialsName,
-            gcp_integration_id: res.data.id,
-            url: this.state.url,
-          },
-          {
-            id: currentProject.id,
-          }
-        )
-      )
-      .then((res) => {
-        this.props.closeForm();
-      })
-      .catch(this.catchError);
-  };
-
-  render() {
-    return (
-      <StyledForm>
-        <CredentialWrapper>
-          <Heading>Porter Settings</Heading>
-          <Helper>
-            Give a name to this set of registry credentials (just for Porter).
-          </Helper>
-          <InputRow
-            type="text"
-            value={this.state.credentialsName}
-            setValue={(credentialsName: string) =>
-              this.setState({ credentialsName })
-            }
-            label="🏷️ Registry Name"
-            placeholder="ex: paper-straw"
-            width="100%"
-          />
-          <Heading>GCP Settings</Heading>
-          <Helper>Service account credentials for GCP permissions.</Helper>
-          <TextArea
-            value={this.state.serviceAccountKey}
-            setValue={(serviceAccountKey: string) =>
-              this.setState({ serviceAccountKey })
-            }
-            label="🔑 Service Account Key (JSON)"
-            placeholder="(Paste your JSON service account key here)"
-            width="100%"
-          />
-          <InputRow
-            type="text"
-            value={this.state.gcpProjectID}
-            setValue={(gcpProjectID: string) => this.setState({ gcpProjectID })}
-            label="📝 GCP Project ID"
-            placeholder="ex: skynet-dev-172969"
-            width="100%"
-          />
-          <InputRow
-            type="text"
-            value={this.state.url}
-            setValue={(url: string) => this.setState({ url })}
-            label="🔗 GCR URL"
-            placeholder="ex: gcr.io/skynet-dev-172969"
-            width="100%"
-          />
-        </CredentialWrapper>
-        <SaveButton
-          text="Save Settings"
-          makeFlush={true}
-          disabled={this.isDisabled()}
-          onClick={this.isDisabled() ? null : this.handleSubmit}
-        />
-      </StyledForm>
-    );
-  }
-}
-
-GCRForm.contextType = Context;
-
-const CredentialWrapper = styled.div`
-  padding: 5px 40px 25px;
-  background: #ffffff11;
-  border-radius: 5px;
-`;
-
-const StyledForm = styled.div`
-  position: relative;
-  padding-bottom: 75px;
-`;

+ 0 - 111
dashboard/src/main/home/integrations/edit-integration/GKEForm.tsx

@@ -1,111 +0,0 @@
-import React, { Component } from "react";
-import styled from "styled-components";
-
-import InputRow from "components/form-components/InputRow";
-import TextArea from "components/form-components/TextArea";
-import SaveButton from "components/SaveButton";
-import Heading from "components/form-components/Heading";
-import Helper from "components/form-components/Helper";
-
-type PropsType = {
-  closeForm: () => void;
-};
-
-type StateType = {
-  clusterName: string;
-  clusterEndpoint: string;
-  clusterCA: string;
-  serviceAccountKey: string;
-};
-
-export default class GKEForm extends Component<PropsType, StateType> {
-  state = {
-    clusterName: "",
-    clusterEndpoint: "",
-    clusterCA: "",
-    serviceAccountKey: "",
-  };
-
-  isDisabled = (): boolean => {
-    let {
-      clusterName,
-      clusterEndpoint,
-      clusterCA,
-      serviceAccountKey,
-    } = this.state;
-    if (
-      clusterName === "" ||
-      clusterEndpoint === "" ||
-      clusterCA === "" ||
-      serviceAccountKey === ""
-    ) {
-      return true;
-    }
-    return false;
-  };
-
-  handleSubmit = () => {
-    // TODO: implement once api is restructured
-  };
-
-  render() {
-    return (
-      <StyledForm>
-        <CredentialWrapper>
-          <Heading>Cluster Settings</Heading>
-          <Helper>Credentials for accessing your GKE cluster.</Helper>
-          <InputRow
-            type="text"
-            value={this.state.clusterName}
-            setValue={(x: string) => this.setState({ clusterName: x })}
-            label="🏷️ Cluster Name"
-            placeholder="ex: briny-pagelet"
-            width="100%"
-          />
-          <InputRow
-            type="text"
-            value={this.state.clusterEndpoint}
-            setValue={(x: string) => this.setState({ clusterEndpoint: x })}
-            label="🌐 Cluster Endpoint"
-            placeholder="ex: 00.00.000.00"
-            width="100%"
-          />
-          <TextArea
-            value={this.state.clusterCA}
-            setValue={(x: string) => this.setState({ clusterCA: x })}
-            label="🔏 Cluster Certificate"
-            placeholder="(Paste your certificate here)"
-            width="100%"
-          />
-
-          <Heading>GCP Settings</Heading>
-          <Helper>Service account credentials for GCP permissions.</Helper>
-          <TextArea
-            value={this.state.serviceAccountKey}
-            setValue={(x: string) => this.setState({ serviceAccountKey: x })}
-            label="🔑 Service Account Key (JSON)"
-            placeholder="(Paste your JSON service account key here)"
-            width="100%"
-          />
-        </CredentialWrapper>
-        <SaveButton
-          text="Save Settings"
-          makeFlush={true}
-          disabled={this.isDisabled()}
-          onClick={this.isDisabled() ? null : this.handleSubmit}
-        />
-      </StyledForm>
-    );
-  }
-}
-
-const CredentialWrapper = styled.div`
-  padding: 5px 40px 25px;
-  background: #ffffff11;
-  border-radius: 5px;
-`;
-
-const StyledForm = styled.div`
-  position: relative;
-  padding-bottom: 75px;
-`;

+ 0 - 240
dashboard/src/main/home/integrations/edit-integration/GitlabIntegrationList.tsx

@@ -1,240 +0,0 @@
-import React, { useContext, useRef, useState } from "react";
-import ConfirmOverlay from "../../../components/ConfirmOverlay";
-import styled from "styled-components";
-import { Context } from "../../../shared/Context";
-import api from "../../../shared/api";
-import { integrationList } from "shared/common";
-import DynamicLink from "components/DynamicLink";
-
-interface Props {
-  gitlabData: any[];
-  updateIntegrationList: () => void;
-}
-
-type StateType = {
-  isDelete: boolean;
-  deleteName: string;
-  deleteID: number;
-};
-
-const GitlabIntegrationList: React.FC<Props> = (props) => {
-  const [currentState, setCurrentState] = useState<StateType>({
-    isDelete: false,
-    deleteName: "",
-    deleteID: 0,
-  });
-
-  const { currentCluster, currentProject, setCurrentError } = useContext(
-    Context
-  );
-
-  const handleDeleteIntegration = () => {
-    api
-      .deleteGitlabIntegration(
-        "<token>",
-        {},
-        {
-          project_id: currentProject.id,
-          integration_id: currentState.deleteID,
-        }
-      )
-      .then(() => {
-        setCurrentState({
-          isDelete: false,
-          deleteName: "",
-          deleteID: 0,
-        });
-        props.updateIntegrationList();
-      })
-      .catch((err) => {
-        setCurrentError(err);
-      });
-  };
-
-  return (
-    <>
-      <ConfirmOverlay
-        show={currentState.isDelete}
-        message={`Are you sure you want to delete the GitLab integration for instance ${currentState.deleteName}?`}
-        onYes={handleDeleteIntegration}
-        onNo={() =>
-          setCurrentState({
-            isDelete: false,
-            deleteName: "",
-            deleteID: 0,
-          })
-        }
-      />
-      <StyledIntegrationList>
-        {props.gitlabData?.length > 0 ? (
-          props.gitlabData.map((inst, idx) => {
-            return (
-              <Integration onClick={() => {}} disabled={false} key={inst.id}>
-                <MainRow disabled={false}>
-                  <Flex>
-                    <Icon src={integrationList.gitlab.icon} />
-                    <Label>{inst.instance_url}</Label>
-                    {inst.username.includes("Unable") ? (
-                      <ErrorLabel>[{inst.username}]</ErrorLabel>
-                    ) : (
-                      <UsernameLabel>({inst.username})</UsernameLabel>
-                    )}
-                  </Flex>
-                  <MaterialIconTray disabled={false}>
-                    <i
-                      className="material-icons"
-                      onClick={() => {
-                        setCurrentState({
-                          isDelete: true,
-                          deleteName: inst.instance_url,
-                          deleteID: inst.id,
-                        });
-                      }}
-                    >
-                      delete
-                    </i>
-                    <i
-                      className="material-icons"
-                      onClick={() => {
-                        window.open(inst.instance_url, "_blank");
-                      }}
-                    >
-                      launch
-                    </i>
-                  </MaterialIconTray>
-                </MainRow>
-              </Integration>
-            );
-          })
-        ) : (
-          <Placeholder>No GitLab instances found</Placeholder>
-        )}
-      </StyledIntegrationList>
-    </>
-  );
-};
-
-export default GitlabIntegrationList;
-
-const Placeholder = styled.div`
-  width: 100%;
-  height: 250px;
-  display: flex;
-  align-items: center;
-  font-size: 13px;
-  font-family: "Work Sans", sans-serif;
-  justify-content: center;
-  margin-top: 30px;
-  background: #ffffff11;
-  color: #ffffff44;
-  border-radius: 5px;
-`;
-
-const Label = styled.div`
-  color: #ffffff;
-  font-size: 14px;
-  font-weight: 500;
-`;
-
-const UsernameLabel = styled.div`
-  color: #ffffff66;
-  font-size: 14px;
-  font-weight: 500;
-  padding: 10px;
-`;
-
-const ErrorLabel = styled.div`
-  color: #f6685e;
-  font-size: 14px;
-  font-weight: 500;
-  padding: 10px;
-`;
-
-const StyledIntegrationList = styled.div`
-  margin-top: 20px;
-  margin-bottom: 80px;
-`;
-
-const MainRow = styled.div`
-  height: 70px;
-  width: 100%;
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  padding: 25px;
-  border-radius: 5px;
-  :hover {
-    background: ${(props: { disabled: boolean }) =>
-      props.disabled ? "" : "#ffffff11"};
-    > i {
-      background: ${(props: { disabled: boolean }) =>
-        props.disabled ? "" : "#ffffff11"};
-    }
-  }
-
-  > i {
-    border-radius: 20px;
-    font-size: 18px;
-    padding: 5px;
-    color: #ffffff44;
-    margin-right: -7px;
-    :hover {
-      background: ${(props: { disabled: boolean }) =>
-        props.disabled ? "" : "#ffffff11"};
-    }
-  }
-`;
-
-const Integration = styled.div`
-  margin-left: -2px;
-  display: flex;
-  flex-direction: column;
-  background: #26282f;
-  cursor: ${(props: { disabled: boolean }) =>
-    props.disabled ? "not-allowed" : "pointer"};
-  margin-bottom: 15px;
-  border-radius: 8px;
-  box-shadow: 0 4px 15px 0px #00000055;
-`;
-
-const Icon = styled.img`
-  width: 27px;
-  margin-right: 12px;
-  margin-bottom: -1px;
-`;
-
-const Flex = styled.div`
-  display: flex;
-  align-items: center;
-
-  > i {
-    cursor: pointer;
-    font-size: 24px;
-    color: #969fbbaa;
-    padding: 3px;
-    margin-right: 11px;
-    border-radius: 100px;
-    :hover {
-      background: #ffffff11;
-    }
-  }
-`;
-
-const MaterialIconTray = styled.div`
-  max-width: 60px;
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  > i {
-    background: #26282f;
-    border-radius: 20px;
-    font-size: 18px;
-    padding: 5px;
-    margin: 0 5px;
-    color: #ffffff44;
-    :hover {
-      background: ${(props: { disabled: boolean }) =>
-        props.disabled ? "" : "#ffffff11"};
-    }
-  }
-`;

+ 0 - 13
dashboard/src/main/home/launch/Boilerplate.tsx

@@ -1,13 +0,0 @@
-import React, { useState } from "react";
-
-import styled from "styled-components";
-
-type Props = {};
-
-export const Boilerplate: React.FC<Props> = (props) => {
-  const [someState, setSomeState] = useState("");
-
-  return <StyledBoilerplate></StyledBoilerplate>;
-};
-
-const StyledBoilerplate = styled.div``;

+ 0 - 281
dashboard/src/main/home/navbar/Feedback.tsx

@@ -1,281 +0,0 @@
-import React, { Component } from "react";
-import styled from "styled-components";
-
-import { Context } from "shared/Context";
-import { handleSubmitFeedback } from "shared/feedback";
-
-type PropsType = {
-  currentView: string;
-};
-
-type StateType = {
-  feedbackSent: boolean;
-  showFeedbackDropdown: boolean;
-  feedbackText: string;
-};
-
-export default class Feedback extends Component<PropsType, StateType> {
-  state = {
-    feedbackSent: false,
-    showFeedbackDropdown: false,
-    feedbackText: "",
-  };
-
-  renderReceipt = () => {
-    if (this.state.feedbackSent) {
-      return (
-        <DropdownAlt dropdownWidth="300px" dropdownMaxHeight="200px">
-          <ConfirmationMessage>
-            <i className="material-icons-outlined">emoji_food_beverage</i>
-            Thanks for improving Porter.
-          </ConfirmationMessage>
-        </DropdownAlt>
-      );
-    }
-  };
-
-  onSubmitFeedback = () => {
-    let { user } = this.context;
-    let msg =
-      "Feedback from " +
-      user.email +
-      " on route " +
-      window.location.pathname +
-      ": " +
-      this.state.feedbackText;
-    handleSubmitFeedback(msg, () => {
-      // console.log("submitted")
-    });
-    this.setState({ feedbackSent: true, feedbackText: "" });
-  };
-
-  renderFeedbackDropdown = () => {
-    if (this.state.showFeedbackDropdown) {
-      let disabled = this.state.feedbackText === "";
-      return (
-        <>
-          <CloseOverlay
-            onClick={() =>
-              this.setState({
-                showFeedbackDropdown: false,
-                feedbackSent: false,
-              })
-            }
-          />
-          <Dropdown
-            feedbackSent={this.state.feedbackSent}
-            dropdownWidth="300px"
-            dropdownMaxHeight="200px"
-          >
-            <FeedbackInput
-              autoFocus={true}
-              value={this.state.feedbackText}
-              onChange={(e) => this.setState({ feedbackText: e.target.value })}
-              placeholder="Help us improve this page."
-            />
-            <SendButton
-              disabled={disabled}
-              onClick={() => !disabled && this.onSubmitFeedback()}
-            >
-              <i className="material-icons">send</i> Send
-            </SendButton>
-          </Dropdown>
-          {this.renderReceipt()}
-        </>
-      );
-    }
-  };
-
-  render() {
-    return (
-      <FeedbackButton>
-        <Flex
-          onClick={() =>
-            this.setState({
-              showFeedbackDropdown: !this.state.showFeedbackDropdown,
-            })
-          }
-        >
-          <i className="material-icons-outlined">campaign</i>
-          Feedback?
-        </Flex>
-        {this.renderFeedbackDropdown()}
-      </FeedbackButton>
-    );
-  }
-}
-
-Feedback.contextType = Context;
-
-const CloseOverlay = styled.div`
-  position: fixed;
-  width: 100vw;
-  height: 100vh;
-  z-index: 100;
-  top: 0;
-  left: 0;
-  cursor: default;
-`;
-
-const ConfirmationMessage = styled.div`
-  width: 100%;
-  height: 100px;
-  display: flex;
-  font-size: 13px;
-  align-items: center;
-  justify-content: center;
-  color: #ffffff55;
-
-  > i {
-    display: flex;
-    font-size: 16px;
-    margin-right: 10px;
-    align-items: center;
-    justify-content: center;
-    color: #ffffff55;
-  }
-`;
-
-const SendButton = styled.div`
-  display: flex;
-  align-items: center;
-  height: 40px;
-  cursor: ${(props: { disabled: boolean }) =>
-    props.disabled ? "not-allowed" : "pointer"};
-  justify-content: center;
-  margin-top: -3px;
-  font-size: 13px;
-  font-weight: 500;
-  font-family: "Work Sans", sans-serif;
-  :hover {
-    background: ${(props: { disabled: boolean }) =>
-      props.disabled ? "" : "#ffffff11"};
-  }
-
-  > i {
-    background: none;
-    border-radius: 3px;
-    display: flex;
-    font-size: 14px;
-    top: 11px;
-    margin-right: 10px;
-    padding: 1px;
-    align-items: center;
-    justify-content: center;
-    color: #ffffffaa;
-  }
-`;
-
-const FeedbackInput = styled.textarea`
-  resize: none;
-  width: 100%;
-  height: 80px;
-  outline: 0;
-  padding: 14px;
-  color: white;
-  border: 0;
-  font-size: 13px;
-  font-family: "Work Sans", sans-serif;
-  background: #aaaabb11;
-`;
-
-const Flex = styled.div`
-  display: flex;
-  align-items: center;
-  cursor: pointer;
-`;
-
-const Dropdown = styled.div`
-  position: absolute;
-  right: 0;
-  top: calc(100% + 5px);
-  background: #26282f;
-  width: ${(props: {
-    dropdownWidth: string;
-    dropdownMaxHeight: string;
-    feedbackSent?: boolean;
-  }) => props.dropdownWidth};
-  max-height: ${(props: {
-    dropdownWidth: string;
-    dropdownMaxHeight: string;
-    feedbackSent?: boolean;
-  }) => (props.dropdownMaxHeight ? props.dropdownMaxHeight : "300px")};
-  border-radius: 3px;
-  z-index: 999;
-  overflow-y: auto;
-  margin-bottom: 20px;
-  box-shadow: 0 8px 20px 0px #00000088;
-  animation: ${(props: {
-    dropdownWidth: string;
-    dropdownMaxHeight: string;
-    feedbackSent?: boolean;
-  }) => (props.feedbackSent ? "flyOff 0.3s 0.05s" : "")};
-  animation-fill-mode: forwards;
-  @keyframes flyOff {
-    from {
-      opacity: 1;
-    }
-    to {
-      opacity: 0;
-    }
-  }
-`;
-
-const DropdownAlt = styled(Dropdown)`
-  animation: fadeIn 0.3s 0.5s;
-  opacity: 0;
-  animation-fill-mode: forwards;
-  @keyframes fadeIn {
-    from {
-      opacity: 0;
-    }
-    to {
-      opacity: 1;
-    }
-  }
-`;
-
-const NavButton = styled.a`
-  display: flex;
-  position: relative;
-  align-items: center;
-  justify-content: center;
-  margin-right: 15px;
-  :hover {
-    > i {
-      color: #ffffff;
-    }
-  }
-
-  > i {
-    cursor: pointer;
-    color: ${(props: { selected?: boolean }) =>
-      props.selected ? "#ffffff" : "#ffffff88"};
-    font-size: 24px;
-  }
-`;
-
-const FeedbackButton = styled(NavButton)`
-  color: ${(props: { selected?: boolean }) =>
-    props.selected ? "#ffffff" : "#ffffff88"};
-  font-family: "Work Sans", sans-serif;
-  font-size: 13px;
-  margin-right: 20px;
-  :hover {
-    color: #ffffff;
-    > div {
-      > i {
-        color: #ffffff;
-      }
-    }
-  }
-
-  > div {
-    > i {
-      color: ${(props: { selected?: boolean }) =>
-        props.selected ? "#ffffff" : "#ffffff88"};
-      font-size: 23px;
-      margin-right: 6px;
-    }
-  }
-`;

+ 0 - 56
dashboard/src/main/home/project-settings/Bars copy.tsx

@@ -1,56 +0,0 @@
-import React, { useState } from "react";
-import {
-  Bar,
-  BarChart,
-  CartesianGrid,
-  Legend,
-  Rectangle,
-  ResponsiveContainer,
-  Tooltip,
-  XAxis,
-  YAxis,
-} from "recharts";
-import styled from "styled-components";
-
-import Text from "components/porter/Text";
-
-type Props = {
-  data: any;
-  yKey: string;
-  xKey: string;
-  fill?: string;
-  title?: string;
-};
-
-const Bars: React.FC<Props> = ({ data, yKey, xKey, fill, title }) => {
-  return (
-    <ResponsiveContainer width="100%" height="100%">
-      <BarChart
-        width={500}
-        height={300}
-        data={data}
-        margin={{
-          top: 5,
-          right: 0,
-          left: -20,
-          bottom: 5,
-        }}
-      >
-        <CartesianGrid vertical={false} stroke="#ffffff22" />
-        <XAxis dataKey={xKey} tick={{ fontSize: 13 }} />
-        <YAxis tick={{ fontSize: 13 }} />
-        <Tooltip wrapperStyle={{ background: "red" }} />
-        <Bar dataKey={yKey} fill={fill || "#6A7FC4"} />
-      </BarChart>
-      <Center>
-        <Text color="helper">{title}</Text>
-      </Center>
-    </ResponsiveContainer>
-  );
-};
-
-export default Bars;
-
-const Center = styled.div`
-  text-align: center;
-`;

+ 0 - 99
dashboard/src/main/home/provisioner/InfraStatuses.tsx

@@ -1,99 +0,0 @@
-import React, { Component } from "react";
-import styled from "styled-components";
-
-import loadingDots from "assets/loading-dots.gif";
-import { InfraType } from "shared/types";
-import { infraNames } from "shared/common";
-
-type PropsType = {
-  infras: InfraType[];
-  selectInfra: (infra: InfraType) => void;
-  selectedInfra: InfraType;
-};
-
-type StateType = {};
-
-export default class InfraStatuses extends Component<PropsType, StateType> {
-  state = {};
-
-  renderStatusIcon = (status: string) => {
-    if (status === "created") {
-      return <StatusIcon>✓</StatusIcon>;
-    } else if (status === "creating" || status === "destroying") {
-      return (
-        <StatusIcon>
-          <img src={loadingDots} />
-        </StatusIcon>
-      );
-    } else if (status === "error" || status === "destroyed") {
-      return <StatusIcon color="#e3366d">✗</StatusIcon>;
-    }
-  };
-
-  render() {
-    return (
-      <StyledInfraStatuses>
-        {this.props.infras.map((infra: InfraType, i: number) => {
-          return (
-            <InfraRow
-              key={infra.id}
-              selected={infra.id === this.props.selectedInfra?.id}
-              onClick={() => this.props.selectInfra(infra)}
-            >
-              {infraNames[infra.kind]}
-              {this.renderStatusIcon(infra.status)}
-            </InfraRow>
-          );
-        })}
-      </StyledInfraStatuses>
-    );
-  }
-}
-
-const StatusIcon = styled.div<{ color?: string }>`
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  width: 20px;
-  font-size: 16px;
-  color: ${(props) => (props.color ? props.color : "#68c49c")};
-  margin-left: 10px;
-`;
-
-const Tab = styled.div`
-  width: 100%;
-  height: 25px;
-  padding-left: 2px;
-  margin-top: 10px;
-  font-size: 13px;
-  color: #aaaabb;
-  color: white;
-  display: flex;
-  align-items: center;
-`;
-
-const InfraRow = styled.div`
-  width: 100%;
-  height: 50px;
-  position: relative;
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  color: ${(props: { selected: boolean }) =>
-    props.selected ? "white" : "#ffffff66"};
-  background: ${(props: { selected: boolean }) =>
-    props.selected ? "#ffffff18" : ""};
-  font-size: 13px;
-  padding: 20px 19px 20px 42px;
-  text-shadow: 0px 0px 8px none;
-  overflow: visible;
-  cursor: pointer;
-  :hover {
-    color: white;
-    background: #ffffff18;
-  }
-`;
-
-const StyledInfraStatuses = styled.div`
-  margin-bottom: 0;
-`;

+ 0 - 191
dashboard/src/main/home/provisioner/Provisioner.tsx

@@ -1,191 +0,0 @@
-import React, { Component } from "react";
-import styled from "styled-components";
-
-import api from "shared/api";
-import { Context } from "shared/Context";
-import { InfraType, ProjectType } from "shared/types";
-import Loading from "components/Loading";
-
-import InfraStatuses from "./InfraStatuses";
-import ProvisionerLogs from "./ProvisionerLogs";
-import { RouteComponentProps, withRouter } from "react-router";
-
-type PropsType = RouteComponentProps & {
-  setRefreshClusters: (x: boolean) => void;
-};
-
-type StateType = {
-  error: boolean;
-  logs: string[];
-  websockets: any[];
-  maxStep: Record<string, number>;
-  currentStep: Record<string, number>;
-  triggerEnd: boolean;
-  infras: InfraType[];
-  loading: boolean;
-  selectedInfra: InfraType;
-  currentProject: ProjectType;
-};
-
-class Provisioner extends Component<PropsType, StateType> {
-  state = {
-    error: false,
-    logs: [] as string[],
-    websockets: [] as any[],
-    maxStep: {} as Record<string, any>,
-    currentStep: {} as Record<string, number>,
-    triggerEnd: false,
-    infras: [] as InfraType[],
-    selectedInfra: null as InfraType,
-    loading: true,
-    currentProject: this.context.currentProject,
-  };
-
-  selectInfra = (infra: InfraType) => {
-    this.setState({ selectedInfra: infra });
-  };
-
-  componentDidMount() {
-    this.updateInfras();
-  }
-
-  componentDidUpdate(prevProps: PropsType, prevState: StateType) {
-    // Check that an infra that was previously in a non-created state, and
-    // which was a cluster, is now in a created state. If so, propagate update
-    // so that cluster can be refreshed.
-    let prevInfraStates: Record<number, string> = {};
-
-    prevState.infras.forEach((infra, i) => {
-      prevInfraStates[infra.id] = infra.status;
-    });
-
-    this.state.infras.forEach((infra, i) => {
-      if (
-        prevInfraStates[infra.id] &&
-        infra.status == "created" &&
-        prevInfraStates[infra.id] != "created"
-      ) {
-        this.props.setRefreshClusters(true);
-      }
-    });
-  }
-
-  refresh = () => {
-    this.updateInfras();
-  };
-
-  updateInfras = () => {
-    let { currentProject } = this.state;
-
-    api
-      .getInfra(
-        "<token>",
-        {},
-        {
-          project_id: currentProject.id,
-        }
-      )
-      .then((res) => {
-        let infras = res.data.sort((a: InfraType, b: InfraType) => {
-          return b.id - a.id;
-        });
-
-        let selectedInfra = this.state.selectedInfra;
-
-        if (!selectedInfra) {
-          selectedInfra = infras[0];
-        }
-
-        this.setState({
-          error: false,
-          infras,
-          loading: false,
-          selectedInfra,
-        });
-      })
-      .catch();
-  };
-
-  render() {
-    if (this.state.loading) {
-      return (
-        <StyledProvisioner>
-          <Loading />
-        </StyledProvisioner>
-      );
-    }
-
-    if (this.state.infras.length > 0) {
-      return (
-        <StyledProvisioner>
-          <TabWrapper>
-            <InfraStatuses
-              infras={this.state.infras}
-              selectInfra={this.selectInfra.bind(this)}
-              selectedInfra={this.state.selectedInfra}
-            />
-          </TabWrapper>
-
-          <ProvisionerLogs
-            key={this.state.selectedInfra?.id}
-            selectedInfra={this.state.selectedInfra}
-            updateInfras={this.updateInfras}
-          />
-        </StyledProvisioner>
-      );
-    }
-
-    return (
-      <StyledProvisioner>
-        You have not provisioned any resources for this project through Porter.{" "}
-        <RefreshText
-          onClick={() => {
-            this.setState({ loading: true });
-            this.refresh();
-          }}
-        >
-          <i className="material-icons">autorenew</i>
-          Refresh
-        </RefreshText>
-      </StyledProvisioner>
-    );
-  }
-}
-
-Provisioner.contextType = Context;
-
-export default withRouter(Provisioner);
-
-const StyledProvisioner = styled.div`
-  width: 100%;
-  height: calc(100vh - 470px);
-  background: #ffffff09;
-  color: #aaaabb;
-  border-radius: 5px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  font-size: 13px;
-  margin-top: 10px;
-`;
-
-const TabWrapper = styled.div`
-  width: 35%;
-  min-width: 250px;
-  height: 100%;
-  overflow-y: auto;
-`;
-
-const RefreshText = styled.div`
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  margin-left: 8px;
-  color: #8590ff;
-  cursor: pointer;
-
-  > i {
-    font-size: 16px;
-    margin-right: 3px;
-  }
-`;

+ 0 - 273
dashboard/src/main/home/provisioner/ProvisionerLogs.tsx

@@ -1,273 +0,0 @@
-import React, { Component } from "react";
-import styled from "styled-components";
-import { Context } from "shared/Context";
-import { InfraType } from "shared/types";
-import { RouteComponentProps, withRouter } from "react-router";
-
-import ansiparse from "shared/ansiparser";
-import loading from "assets/loading.gif";
-
-type PropsType = RouteComponentProps & {
-  selectedInfra: InfraType;
-  updateInfras: () => void;
-};
-
-type StateType = {
-  logs: string[];
-  ws: any;
-  scroll: boolean;
-  maxStep: number;
-  error: boolean;
-};
-
-class ProvisionerLogs extends Component<PropsType, StateType> {
-  state = {
-    logs: [] as string[],
-    ws: null as any,
-    scroll: true,
-    maxStep: 0,
-    error: false,
-  };
-
-  ws = null as any;
-  parentRef = React.createRef<HTMLDivElement>();
-
-  scrollToBottom = () => {
-    this.parentRef.current.lastElementChild.scrollIntoView({
-      behavior: "auto",
-    });
-  };
-
-  renderLogs = () => {
-    let { selectedInfra } = this.props;
-    let { logs, maxStep } = this.state;
-    if (!selectedInfra) {
-      return <Message>Please select a resource.</Message>;
-    }
-
-    // if (selectedInfra.status == "destroyed") {
-    //   return (
-    //     <Message>
-    //       This resource has been auto-destroyed due to an error during
-    //       provisioning.
-    //       <div>
-    //         Please check with your cloud provider to make sure all resources
-    //         have been properly destroyed.
-    //       </div>
-    //     </Message>
-    //   );
-    // }
-
-    if (logs.length == 0) {
-      switch (selectedInfra.status) {
-        case "creating":
-          return (
-            <Loading>
-              <LoadingGif src={loading} /> Provisioning resources...
-            </Loading>
-          );
-        case "destroying":
-          return (
-            <Message>
-              <LoadingGif src={loading} /> Destroying resources...
-            </Message>
-          );
-        case "error":
-          return (
-            <Message>
-              Porter encountered an error while provisioning this resource.
-            </Message>
-          );
-        default:
-          return <Message>{selectedInfra.status}</Message>;
-      }
-    }
-
-    let count = 0;
-    return logs.map((log, i) => {
-      if (log.trim().length != 0) {
-        count += 1;
-        return <Log key={i + 1}>{`[Step ${count}/${maxStep}] ` + log}</Log>;
-      }
-    });
-  };
-
-  isJSON = (str: string) => {
-    try {
-      JSON.parse(str);
-    } catch (e) {
-      return false;
-    }
-    return true;
-  };
-
-  setupWebsocket = () => {
-    this.ws.onopen = () => {
-      console.log("connected to websocket");
-    };
-
-    this.ws.onmessage = (evt: MessageEvent) => {
-      let event = JSON.parse(evt.data);
-      let validEvents = [] as any[];
-      let err = null;
-
-      for (var i = 0; i < event.length; i++) {
-        let msg = event[i];
-        if (
-          msg["Values"] &&
-          msg["Values"]["data"] &&
-          this.isJSON(msg["Values"]["data"])
-        ) {
-          let d = JSON.parse(msg["Values"]["data"]);
-
-          if (d["kind"] == "error") {
-            err = d["log"];
-            break;
-          }
-
-          // add only valid events
-          if (
-            d["log"] != null &&
-            d["created_resources"] != null &&
-            d["total_resources"] != null
-          ) {
-            validEvents.push(d);
-          }
-        }
-      }
-
-      if (err) {
-        window.analytics?.track("Provisioning Error", {
-          error: err,
-        });
-        let e = ansiparse(err).map((el: any) => {
-          return el.text;
-        });
-
-        this.setState({ logs: [...this.state.logs, ...e], error: true });
-        return;
-      }
-
-      if (validEvents.length == 0) {
-        return;
-      }
-
-      let logs = [] as any[];
-      validEvents.forEach((e: any) => {
-        logs.push(...ansiparse(e["log"]));
-      });
-
-      logs = logs.map((log: any) => {
-        return log.text;
-      });
-
-      this.setState(
-        {
-          logs: [...this.state.logs, ...logs],
-          maxStep: validEvents[validEvents.length - 1]["total_resources"],
-        },
-        () => {
-          setTimeout(() => {
-            this.props.updateInfras();
-          }, 3000);
-
-          this.scrollToBottom();
-        }
-      );
-    };
-
-    this.ws.onerror = (err: ErrorEvent) => {
-      console.log("websocket err", err);
-    };
-
-    this.ws.onclose = () => {
-      console.log("closing provisioner websocket");
-    };
-  };
-
-  componentDidMount() {
-    let { currentProject } = this.context;
-    let { selectedInfra } = this.props;
-
-    if (!selectedInfra) return;
-
-    let protocol = window.location.protocol == "https:" ? "wss" : "ws";
-    this.ws = new WebSocket(
-      `${protocol}://${window.location.host}/api/projects/${currentProject.id}/infras/${selectedInfra.id}/logs`
-    );
-
-    this.setupWebsocket();
-    this.scrollToBottom();
-  }
-
-  componentWillUnmount() {
-    if (this.ws) {
-      this.ws.close();
-    }
-  }
-
-  render() {
-    return (
-      <LogStream>
-        <Wrapper ref={this.parentRef}>{this.renderLogs()}</Wrapper>
-      </LogStream>
-    );
-  }
-}
-
-ProvisionerLogs.contextType = Context;
-export default withRouter(ProvisionerLogs);
-
-const Loading = styled.div`
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-  justify-content: center;
-  height: 100%;
-  width: 100%;
-  color: #ffffff44;
-  font-size: 13px;
-`;
-
-const LoadingGif = styled.img`
-  width: 15px;
-  height: 15px;
-  margin-right: 9px;
-  margin-bottom: 0px;
-`;
-
-const Wrapper = styled.div`
-  width: 100%;
-  height: 100%;
-  overflow: auto;
-  padding: 25px 30px;
-`;
-
-const LogStream = styled.div`
-  display: flex;
-  flex-direction: column;
-  flex: 1;
-  float: right;
-  height: 100%;
-  background: #202227;
-  user-select: text;
-  max-width: 65%;
-  overflow-y: auto;
-  overflow-wrap: break-word;
-`;
-
-const Message = styled.div`
-  display: flex;
-  flex-direction: column;
-  height: 100%;
-  width: 100%;
-  align-items: center;
-  justify-content: center;
-  color: #ffffff44;
-  font-size: 13px;
-`;
-
-const Log = styled.div`
-  font-family: monospace;
-  font-size: 12px;
-`;

+ 0 - 140
dashboard/src/main/home/sidebar/AddCluster/AzureCredentialForm.tsx

@@ -1,140 +0,0 @@
-import React, { useContext, useState } from "react";
-import InputRow from "components/form-components/InputRow";
-import SaveButton from "components/SaveButton";
-
-import { Context } from "shared/Context";
-import api from "shared/api";
-import styled from "styled-components";
-import Loading from "components/Loading";
-import Placeholder from "components/OldPlaceholder";
-
-type Props = {
-  setCreatedCredential: (aws_integration_id: number) => void;
-  cancel: () => void;
-};
-
-const AzureCredentialForm: React.FunctionComponent<Props> = ({
-  setCreatedCredential,
-}) => {
-  const { currentProject, setCurrentError } = useContext(Context);
-  const [clientId, setClientId] = useState("");
-  const [servicePrincipalKey, setServicePrincipalKey] = useState("");
-  const [tenantId, setTenantId] = useState("");
-  const [subscriptionId, setSubscriptionId] = useState("");
-  const [buttonStatus, setButtonStatus] = useState("");
-  const [isLoading, setIsLoading] = useState(false);
-  const [hasError, setHasError] = useState(false);
-
-  const submit = () => {
-    setIsLoading(true);
-
-    api
-      .createAzureIntegration(
-        "<token>",
-        {
-          azure_client_id: clientId,
-          azure_subscription_id: subscriptionId,
-          azure_tenant_id: tenantId,
-          service_principal_key: servicePrincipalKey,
-        },
-        {
-          id: currentProject.id,
-        }
-      )
-      .then(({ data }) => {
-        setCreatedCredential(data.id);
-        setIsLoading(false);
-      })
-      .catch((err) => {
-        console.error(err);
-        setHasError(true);
-        setCurrentError(err.response?.data?.error);
-        setIsLoading(false);
-      });
-  };
-
-  if (hasError) {
-    return <Placeholder>Error</Placeholder>;
-  }
-
-  if (isLoading) {
-    return (
-      <Placeholder>
-        <Loading />
-      </Placeholder>
-    );
-  }
-
-  return (
-    <>
-      <InputRow
-        type="text"
-        value={clientId}
-        setValue={(x: string) => {
-          setClientId(x);
-        }}
-        label="👤 Azure Client ID"
-        placeholder="ex. 12345678-abcd-1234-abcd-12345678abcd"
-        width="100%"
-        isRequired={true}
-      />
-      <InputRow
-        type="password"
-        value={servicePrincipalKey}
-        setValue={(x: string) => {
-          setServicePrincipalKey(x);
-        }}
-        label="🔒 Azure Service Principal Key"
-        placeholder="○ ○ ○ ○ ○ ○ ○ ○ ○"
-        width="100%"
-        isRequired={true}
-      />
-      <InputRow
-        type="text"
-        value={tenantId}
-        setValue={(x: string) => {
-          setTenantId(x);
-        }}
-        label="Azure Tenant ID"
-        placeholder="ex. 12345678-abcd-1234-abcd-12345678abcd"
-        width="100%"
-        isRequired={true}
-      />
-      <InputRow
-        type="text"
-        value={subscriptionId}
-        setValue={(x: string) => {
-          setSubscriptionId(x);
-        }}
-        label="Azure Subscription ID"
-        placeholder="ex. 12345678-abcd-1234-abcd-12345678abcd"
-        width="100%"
-        isRequired={true}
-      />
-      <Flex>
-        <SaveButton
-          text="Continue"
-          disabled={false}
-          onClick={submit}
-          makeFlush={true}
-          clearPosition={true}
-          status={buttonStatus}
-          statusPosition={"right"}
-        />
-      </Flex>
-    </>
-  );
-};
-
-export default AzureCredentialForm;
-
-const Flex = styled.div`
-  display: flex;
-  color: #ffffff;
-  align-items: center;
-  > i {
-    color: #aaaabb;
-    font-size: 20px;
-    margin-right: 10px;
-  }
-`;

+ 0 - 110
dashboard/src/main/home/sidebar/AddCluster/AzureCredentialList.tsx

@@ -1,110 +0,0 @@
-import React, { useContext, useEffect, useState } from "react";
-import { Context } from "shared/Context";
-import api from "shared/api";
-import styled from "styled-components";
-import Loading from "components/Loading";
-import Placeholder from "components/OldPlaceholder";
-import AzureCredentialForm from "./AzureCredentialForm";
-import CredentialList from "./CredentialList";
-import Description from "components/Description";
-
-type Props = {
-  selectCredential: (azure_integration_id: number) => void;
-};
-
-type AzureCredential = {
-  created_at: string;
-  id: number;
-  user_id: number;
-  project_id: number;
-  azure_client_id: string;
-};
-
-const AzureCredentialsList: React.FunctionComponent<Props> = ({
-  selectCredential,
-}) => {
-  const { currentProject, setCurrentError } = useContext(Context);
-  const [isLoading, setIsLoading] = useState(true);
-  const [azCredentials, setAzureCredentials] = useState<AzureCredential[]>(
-    null
-  );
-  const [shouldCreateCred, setShouldCreateCred] = useState(false);
-  const [hasError, setHasError] = useState(false);
-
-  useEffect(() => {
-    api
-      .getAzureIntegration(
-        "<token>",
-        {},
-        {
-          project_id: currentProject.id,
-        }
-      )
-      .then(({ data }) => {
-        if (!Array.isArray(data)) {
-          throw Error("Data is not an array");
-        }
-
-        setAzureCredentials(data);
-        setIsLoading(false);
-      })
-      .catch((err) => {
-        console.error(err);
-        setHasError(true);
-        setCurrentError(err.response?.data?.error);
-        setIsLoading(false);
-      });
-  }, [currentProject]);
-
-  if (hasError) {
-    return <Placeholder>Error</Placeholder>;
-  }
-
-  if (isLoading) {
-    return (
-      <Placeholder>
-        <Loading />
-      </Placeholder>
-    );
-  }
-
-  const renderContents = () => {
-    if (shouldCreateCred) {
-      return (
-        <AzureCredentialForm
-          setCreatedCredential={selectCredential}
-          cancel={() => {}}
-        />
-      );
-    }
-
-    return (
-      <>
-        <Description>
-          Select your credentials from the list below, or create a new
-          credential:
-        </Description>
-        <CredentialList
-          credentials={azCredentials.map((cred) => {
-            return {
-              id: cred.id,
-              display_name: cred.azure_client_id,
-              created_at: cred.created_at,
-            };
-          })}
-          selectCredential={selectCredential}
-          shouldCreateCred={() => setShouldCreateCred(true)}
-          addNewText="Add New Azure Credential"
-        />
-      </>
-    );
-  };
-
-  return <AzureCredentialWrapper>{renderContents()}</AzureCredentialWrapper>;
-};
-
-export default AzureCredentialsList;
-
-const AzureCredentialWrapper = styled.div`
-  margin-top: 20px;
-`;

+ 0 - 101
dashboard/src/main/home/sidebar/AddCluster/ClusterList.tsx

@@ -1,101 +0,0 @@
-import React, { useContext, useEffect, useState } from "react";
-import { Context } from "shared/Context";
-import api from "shared/api";
-import Loading from "components/Loading";
-import Placeholder from "components/OldPlaceholder";
-import Description from "components/Description";
-import { ClusterType } from "shared/types";
-import SelectRow from "components/form-components/SelectRow";
-import SaveButton from "components/SaveButton";
-
-type Props = {
-  selectCluster: (cluster_id: number) => void;
-};
-
-const ClusterList: React.FunctionComponent<Props> = ({ selectCluster }) => {
-  const { currentProject, setCurrentError } = useContext(Context);
-  const [isLoading, setIsLoading] = useState(true);
-  const [clusters, setClusters] = useState<ClusterType[]>([]);
-  const [selectedClusterID, setSelectedClusterID] = useState<number>();
-  const [hasError, setHasError] = useState(false);
-
-  useEffect(() => {
-    api
-      .getClusters(
-        "<token>",
-        {},
-        {
-          id: currentProject.id,
-        }
-      )
-      .then(({ data }) => {
-        if (!Array.isArray(data)) {
-          throw Error("Data is not an array");
-        }
-
-        setClusters(data);
-        setSelectedClusterID(data[0]?.id);
-        setIsLoading(false);
-      })
-      .catch((err) => {
-        console.error(err);
-        setHasError(true);
-        setCurrentError(err.response?.data?.error);
-        setIsLoading(false);
-      });
-  }, [currentProject]);
-
-  if (hasError) {
-    return <Placeholder>Error</Placeholder>;
-  }
-
-  if (isLoading || !clusters) {
-    return (
-      <Placeholder>
-        <Loading />
-      </Placeholder>
-    );
-  }
-
-  if (clusters.length == 0) {
-    return (
-      <Placeholder>
-        At least one cluster must exist to create this resource
-      </Placeholder>
-    );
-  }
-
-  return (
-    <>
-      <Description>
-        Select your credentials from the list below, or create a new credential:
-      </Description>
-      <SelectRow
-        options={clusters.map((cluster, i) => {
-          return {
-            label: cluster.name,
-            value: "" + cluster.id,
-          };
-        })}
-        width="100%"
-        scrollBuffer={true}
-        value={"" + selectedClusterID}
-        dropdownMaxHeight="240px"
-        setActiveValue={(x: string) => {
-          setSelectedClusterID(parseInt(x));
-        }}
-        label="Cluster Options"
-      />
-      <SaveButton
-        text="Continue"
-        disabled={false}
-        onClick={() => selectCluster(selectedClusterID)}
-        makeFlush={true}
-        clearPosition={true}
-        statusPosition={"right"}
-      />
-    </>
-  );
-};
-
-export default ClusterList;

+ 0 - 114
dashboard/src/main/home/sidebar/AddCluster/GCPCredentialForm.tsx

@@ -1,114 +0,0 @@
-import React, { useContext, useState } from "react";
-import InputRow from "components/form-components/InputRow";
-import SaveButton from "components/SaveButton";
-
-import { Context } from "shared/Context";
-import api from "shared/api";
-import styled from "styled-components";
-import Loading from "components/Loading";
-import Placeholder from "components/OldPlaceholder";
-import Helper from "components/form-components/Helper";
-import UploadArea from "components/form-components/UploadArea";
-
-type Props = {
-  setCreatedCredential: (aws_integration_id: number) => void;
-  cancel: () => void;
-};
-
-const GCPCredentialForm: React.FunctionComponent<Props> = ({
-  setCreatedCredential,
-}) => {
-  const { currentProject, setCurrentError } = useContext(Context);
-  const [buttonStatus, setButtonStatus] = useState("");
-  const [projectId, setProjectId] = useState("");
-  const [serviceAccountKey, setServiceAccountKey] = useState("");
-  const [isLoading, setIsLoading] = useState(false);
-  const [hasError, setHasError] = useState(false);
-
-  const submit = () => {
-    setIsLoading(true);
-    api
-      .createGCPIntegration(
-        "<token>",
-        {
-          gcp_key_data: serviceAccountKey,
-          gcp_project_id: projectId,
-        },
-        {
-          project_id: currentProject.id,
-        }
-      )
-      .then(({ data }) => {
-        setCreatedCredential(data.id);
-        setIsLoading(false);
-      })
-      .catch((err) => {
-        console.error(err);
-        setHasError(true);
-        setCurrentError(err.response?.data?.error);
-        setIsLoading(false);
-      });
-  };
-
-  if (hasError) {
-    return <Placeholder>Error</Placeholder>;
-  }
-
-  if (isLoading) {
-    return (
-      <Placeholder>
-        <Loading />
-      </Placeholder>
-    );
-  }
-
-  return (
-    <>
-      <InputRow
-        type="text"
-        value={projectId}
-        setValue={(x: string) => {
-          setProjectId(x);
-        }}
-        label="🏷️ GCP Project ID"
-        placeholder="ex: blindfold-ceiling-24601"
-        width="100%"
-        isRequired={true}
-      />
-
-      <Helper>Service account credentials for GCP permissions.</Helper>
-      <UploadArea
-        setValue={(x: any) => setServiceAccountKey(x)}
-        label="🔒 GCP Key Data (JSON)"
-        placeholder="Choose a file or drag it here."
-        width="100%"
-        height="100%"
-        isRequired={true}
-      />
-      <Flex>
-        <SaveButton
-          text="Continue"
-          disabled={false}
-          onClick={submit}
-          makeFlush={true}
-          clearPosition={true}
-          status={buttonStatus}
-          statusPosition={"right"}
-        />
-      </Flex>
-    </>
-  );
-};
-
-export default GCPCredentialForm;
-
-const Flex = styled.div`
-  display: flex;
-  color: #ffffff;
-  align-items: center;
-  > i {
-    color: #aaaabb;
-    font-size: 20px;
-    margin-right: 10px;
-  }
-`;

+ 0 - 108
dashboard/src/main/home/sidebar/AddCluster/GCPCredentialList.tsx

@@ -1,108 +0,0 @@
-import React, { useContext, useEffect, useState } from "react";
-import { Context } from "shared/Context";
-import api from "shared/api";
-import styled from "styled-components";
-import Loading from "components/Loading";
-import Placeholder from "components/OldPlaceholder";
-import GCPCredentialForm from "./GCPCredentialForm";
-import CredentialList from "./CredentialList";
-import Description from "components/Description";
-
-type Props = {
-  selectCredential: (gcp_integration_id: number) => void;
-};
-
-type GCPCredential = {
-  created_at: string;
-  id: number;
-  user_id: number;
-  project_id: number;
-  gcp_sa_email: string;
-};
-
-const GCPCredentialsList: React.FunctionComponent<Props> = ({
-  selectCredential,
-}) => {
-  const { currentProject, setCurrentError } = useContext(Context);
-  const [isLoading, setIsLoading] = useState(true);
-  const [gcpCredentials, setGCPCredentials] = useState<GCPCredential[]>(null);
-  const [shouldCreateCred, setShouldCreateCred] = useState(false);
-  const [hasError, setHasError] = useState(false);
-
-  useEffect(() => {
-    api
-      .getGCPIntegration(
-        "<token>",
-        {},
-        {
-          project_id: currentProject.id,
-        }
-      )
-      .then(({ data }) => {
-        if (!Array.isArray(data)) {
-          throw Error("Data is not an array");
-        }
-
-        setGCPCredentials(data);
-        setIsLoading(false);
-      })
-      .catch((err) => {
-        console.error(err);
-        setHasError(true);
-        setCurrentError(err.response?.data?.error);
-        setIsLoading(false);
-      });
-  }, [currentProject]);
-
-  if (hasError) {
-    return <Placeholder>Error</Placeholder>;
-  }
-
-  if (isLoading) {
-    return (
-      <Placeholder>
-        <Loading />
-      </Placeholder>
-    );
-  }
-
-  const renderContents = () => {
-    if (shouldCreateCred) {
-      return (
-        <GCPCredentialForm
-          setCreatedCredential={selectCredential}
-          cancel={() => {}}
-        />
-      );
-    }
-
-    return (
-      <>
-        <Description>
-          Select your credentials from the list below, or create a new
-          credential:
-        </Description>
-        <CredentialList
-          credentials={gcpCredentials.map((cred) => {
-            return {
-              id: cred.id,
-              display_name: cred.gcp_sa_email,
-              created_at: cred.created_at,
-            };
-          })}
-          selectCredential={selectCredential}
-          shouldCreateCred={() => setShouldCreateCred(true)}
-          addNewText="Add New GCP Credential"
-        />
-      </>
-    );
-  };
-
-  return <GCPCredentialWrapper>{renderContents()}</GCPCredentialWrapper>;
-};
-
-export default GCPCredentialsList;
-
-const GCPCredentialWrapper = styled.div`
-  margin-top: 20px;
-`;

+ 0 - 294
dashboard/src/main/home/sidebar/ProjectSection.tsx

@@ -1,294 +0,0 @@
-import React, { Component } from "react";
-import styled from "styled-components";
-import gradient from "assets/gradient.png";
-
-import api from "shared/api"; 
-import { Context } from "shared/Context";
-import { ProjectListType, ProjectType } from "shared/types";
-import { pushFiltered } from "shared/routing";
-import { RouteComponentProps, withRouter } from "react-router";
-
-type PropsType = RouteComponentProps & {
-  currentProject: ProjectType;
-  projects: ProjectListType[];
-};
-
-type StateType = {
-  expanded: boolean;
-};
-
-class ProjectSection extends Component<PropsType, StateType> {
-  state = {
-    expanded: false,
-  };
-
-  wrapperRef: any = React.createRef();
-
-  componentDidMount() {
-    document.addEventListener("mousedown", this.handleClickOutside.bind(this));
-  }
-
-  componentWillUnmount() {
-    document.removeEventListener(
-      "mousedown",
-      this.handleClickOutside.bind(this)
-    );
-  }
-
-  handleClickOutside = (e: any) => {
-    if (
-      this.wrapperRef &&
-      this.wrapperRef.current &&
-      !this.wrapperRef.current.contains(e.target)
-    ) {
-      this.setState({ expanded: false });
-    }
-  };
-
-  renderOptionList = () => {
-    let { setCurrentProject, setCurrentCluster, currentProject } = this.context;
-    return this.props.projects.map((projectListEntry: ProjectListType, i: number) => {
-      return (
-        <Option
-          key={i}
-          selected={projectListEntry.name === this.props.currentProject.name}
-          onClick={async () => {
-            this.setState({ expanded: false });
-            if (projectListEntry.id !== currentProject.id) {
-              setCurrentCluster(null);
-            }
-
-            const project = await api
-              .getProject("<token>", {}, { id: projectListEntry.id })
-              .then((res) => res.data as ProjectType);
-
-            setCurrentProject(project, () => {
-              pushFiltered(this.props, "/dashboard", ["project_id"]);
-            });
-          }}
-        >
-          <ProjectIcon>
-            <ProjectImage src={gradient} />
-            <Letter>{projectListEntry.name[0].toUpperCase()}</Letter>
-          </ProjectIcon>
-          <ProjectLabel>{projectListEntry.name}</ProjectLabel>
-        </Option>
-      );
-    });
-  };
-
-  renderDropdown = () => {
-    if (this.state.expanded) {
-      return (
-        <div>
-          <Dropdown>
-            {this.renderOptionList()}
-            {this.context.user?.email.includes("porter.run") && (
-              <Option
-                selected={false}
-                lastItem={true}
-                onClick={() =>
-                  pushFiltered(this.props, "/new-project", ["project_id"])
-                }
-              >
-                <ProjectIconAlt>+</ProjectIconAlt>
-                <ProjectLabel>Create a project</ProjectLabel>
-              </Option>
-            )}
-          </Dropdown>
-        </div>
-      );
-    }
-  };
-
-  handleExpand = () => {
-    this.setState({ expanded: !this.state.expanded });
-  };
-
-  render() {
-    let { currentProject } = this.props;
-    if (currentProject) {
-      return (
-        <StyledProjectSection ref={this.wrapperRef}>
-          <MainSelector
-            onClick={this.handleExpand}
-            expanded={this.state.expanded}
-          >
-            <ProjectIcon>
-              <ProjectImage src={gradient} />
-              <Letter>{currentProject.name[0].toUpperCase()}</Letter>
-            </ProjectIcon>
-            <ProjectName>{currentProject.name}</ProjectName>
-            <i className="material-icons">arrow_drop_down</i>
-          </MainSelector>
-          {this.renderDropdown()}
-        </StyledProjectSection>
-      );
-    }
-    return (
-      <InitializeButton
-        onClick={() =>
-          pushFiltered(this.props, "/new-project", ["project_id"], {
-            new_project: true,
-          })
-        }
-      >
-        <Plus>+</Plus> Create a project
-      </InitializeButton>
-    );
-  }
-}
-
-ProjectSection.contextType = Context;
-
-export default withRouter(ProjectSection);
-
-const ProjectLabel = styled.div`
-  overflow: hidden;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-`;
-
-const Plus = styled.div`
-  margin-right: 10px;
-  font-size: 15px;
-`;
-
-const InitializeButton = styled.div`
-  position: relative;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  width: calc(100% - 30px);
-  height: 38px;
-  margin: 8px 15px;
-  font-size: 13px;
-  font-weight: 500;
-  border-radius: 3px;
-  color: ${props => props.theme.text.primary};
-  padding-bottom: 1px;
-  cursor: pointer;
-  background: #ffffff11;
-
-  :hover {
-    background: #ffffff22;
-  }
-`;
-
-const Option = styled.div`
-  width: 100%;
-  border-top: 1px solid #00000000;
-  border-bottom: 1px solid
-    ${(props: { selected: boolean; lastItem?: boolean }) =>
-    props.lastItem ? "#ffffff00" : "#ffffff15"};
-  height: 45px;
-  display: flex;
-  align-items: center;
-  font-size: 13px;
-  align-items: center;
-  padding-left: 10px;
-  cursor: pointer;
-  padding-right: 10px;
-  background: ${(props: { selected: boolean; lastItem?: boolean }) =>
-    props.selected ? "#ffffff11" : ""};
-  :hover {
-    background: ${(props: { selected: boolean; lastItem?: boolean }) =>
-    props.selected ? "" : "#ffffff22"};
-  }
-
-  > i {
-    font-size: 18px;
-    margin-right: 12px;
-    margin-left: 5px;
-    color: #ffffff44;
-  }
-`;
-
-const Dropdown = styled.div`
-  position: absolute;
-  right: 13px;
-  top: calc(100% + 5px);
-  background: #26282f;
-  width: 210px;
-  max-height: 500px;
-  border-radius: 3px;
-  z-index: 999;
-  overflow-y: auto;
-  margin-bottom: 20px;
-  box-shadow: 0 5px 15px 5px #00000077;
-`;
-
-const ProjectName = styled.div`
-  overflow: hidden;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-`;
-
-const Letter = styled.div`
-  height: 100%;
-  width: 100%;
-  position: absolute;
-  padding-bottom: 2px;
-  font-weight: 500;
-  top: 0;
-  left: 0;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-`;
-
-const ProjectImage = styled.img`
-  width: 100%;
-  height: 100%;
-`;
-
-const ProjectIcon = styled.div`
-  width: 25px;
-  min-width: 25px;
-  height: 25px;
-  border-radius: 3px;
-  overflow: hidden;
-  position: relative;
-  margin-right: 10px;
-  font-weight: 400;
-`;
-
-const ProjectIconAlt = styled(ProjectIcon)`
-  border: 1px solid #ffffff44;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-`;
-
-const StyledProjectSection = styled.div`
-  position: relative;
-  margin-left: 3px;
-  color: ${props => props.theme.text.primary};
-`;
-
-const MainSelector = styled.div`
-  display: flex;
-  align-items: center;
-  margin: 10px 0 0;
-  font-size: 14px;
-  cursor: pointer;
-  padding: 10px 0;
-  padding-left: 20px;
-  :hover {
-    > i {
-      background: #ffffff22;
-    }
-  }
-
-  > i {
-    margin-left: 7px;
-    margin-right: 12px;
-    font-size: 20px;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    border-radius: 20px;
-    background: ${(props: { expanded: boolean }) =>
-    props.expanded ? "#ffffff22" : ""};
-  }
-`;

+ 0 - 22
dashboard/src/shared/feedback.tsx

@@ -1,22 +0,0 @@
-import axios from "axios";
-
-export const handleSubmitFeedback = (
-  msg: string,
-  callback?: (err: any, res: any) => void
-) => {
-  axios
-    .get(
-      process.env.FEEDBACK_ENDPOINT,
-      {
-        params: {
-          message: msg,
-        }
-      },
-    )
-    .then((res) => {
-      callback && callback(null, res);
-    })
-    .catch((err) => {
-      callback && callback(err, null);
-    });
-};