Explorar el Código

Started implementing environment card

jnfrati hace 4 años
padre
commit
34d9c27e1e

+ 91 - 0
dashboard/src/components/OptionsDropdown.tsx

@@ -0,0 +1,91 @@
+import React, { useState } from "react";
+import styled from "styled-components";
+
+export const OptionsDropdown: React.FC<{
+  expandIcon?: string;
+  shrinkIcon?: string;
+}> = ({ children, expandIcon = "expand_more", shrinkIcon = "expand_less" }) => {
+  const [isOpen, setIsOpen] = useState(false);
+
+  const handleClick = (e: any) => {
+    e.stopPropagation();
+    setIsOpen(!isOpen);
+  };
+
+  const handleOnBlur = () => {
+    setIsOpen(false);
+  };
+
+  return (
+    <OptionsButton onClick={handleClick} onBlur={handleOnBlur}>
+      <i className="material-icons">
+        {isOpen ? { shrinkIcon } : { expandIcon }}
+      </i>
+      {isOpen && <DropdownMenu>{children}</DropdownMenu>}
+    </OptionsButton>
+  );
+};
+
+const OptionsButton = styled.button`
+  position: relative;
+  border: none;
+  background: none;
+  color: white;
+  padding: 5px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  border-radius: 50%;
+  color: #ffffff44;
+  :hover {
+    background: #32343a;
+    cursor: pointer;
+  }
+`;
+
+const DropdownMenu = styled.div`
+  position: absolute;
+  right: 12px;
+  top: 30px;
+  overflow: hidden;
+  width: 120px;
+  height: auto;
+  background: #26282f;
+  box-shadow: 0 8px 20px 0px #00000088;
+  color: white;
+`;
+
+const DropdownOption = styled.div`
+  width: 100%;
+  height: 37px;
+  font-size: 13px;
+  cursor: pointer;
+  padding-left: 10px;
+  padding-right: 10px;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  :hover {
+    background: #ffffff22;
+  }
+  :not(:first-child) {
+    border-top: 1px solid #00000000;
+  }
+
+  :not(:last-child) {
+    border-bottom: 1px solid #ffffff15;
+  }
+
+  > i {
+    margin-right: 5px;
+    font-size: 16px;
+  }
+`;
+
+export default {
+  Dropdown: OptionsDropdown,
+  Option: DropdownOption,
+};

+ 5 - 82
dashboard/src/main/home/cluster-dashboard/dashboard/NamespaceList.tsx

@@ -6,25 +6,7 @@ import { pushFiltered } from "shared/routing";
 import { useHistory, useLocation } from "react-router";
 import { useHistory, useLocation } from "react-router";
 import useAuth from "shared/auth/useAuth";
 import useAuth from "shared/auth/useAuth";
 
 
-const OptionsDropdown: React.FC = ({ children }) => {
-  const [isOpen, setIsOpen] = useState(false);
-
-  const handleClick = (e: any) => {
-    e.stopPropagation();
-    setIsOpen(!isOpen);
-  };
-
-  const handleOnBlur = () => {
-    setIsOpen(false);
-  };
-
-  return (
-    <OptionsButton onClick={handleClick} onBlur={handleOnBlur}>
-      <i className="material-icons">{isOpen ? "expand_less" : "expand_more"}</i>
-      {isOpen && <DropdownMenu>{children}</DropdownMenu>}
-    </OptionsButton>
-  );
-};
+import OptionsDropdown from "components/OptionsDropdown";
 
 
 const useWebsocket = (
 const useWebsocket = (
   currentProject: ProjectType,
   currentProject: ProjectType,
@@ -173,12 +155,12 @@ export const NamespaceList: React.FunctionComponent = () => {
               {isAuthorized("namespace", "", ["get", "delete"]) &&
               {isAuthorized("namespace", "", ["get", "delete"]) &&
                 isAvailableForDeletion(namespace?.metadata?.name) &&
                 isAvailableForDeletion(namespace?.metadata?.name) &&
                 namespace?.status?.phase === "Active" && (
                 namespace?.status?.phase === "Active" && (
-                  <OptionsDropdown>
-                    <DropdownOption onClick={() => onDelete(namespace)}>
+                  <OptionsDropdown.Dropdown>
+                    <OptionsDropdown.Option onClick={() => onDelete(namespace)}>
                       <i className="material-icons-outlined">delete</i>
                       <i className="material-icons-outlined">delete</i>
                       <span>Delete</span>
                       <span>Delete</span>
-                    </DropdownOption>
-                  </OptionsDropdown>
+                    </OptionsDropdown.Option>
+                  </OptionsDropdown.Dropdown>
                 )}
                 )}
             </StyledCard>
             </StyledCard>
           );
           );
@@ -333,62 +315,3 @@ const ContentContainer = styled.div`
   justify-content: space-between;
   justify-content: space-between;
   height: 100%;
   height: 100%;
 `;
 `;
-
-const OptionsButton = styled.button`
-  position: relative;
-  border: none;
-  background: none;
-  color: white;
-  padding: 5px;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  border-radius: 50%;
-  color: #ffffff44;
-  :hover {
-    background: #32343a;
-    cursor: pointer;
-  }
-`;
-
-const DropdownMenu = styled.div`
-  position: absolute;
-  right: 12px;
-  top: 30px;
-  overflow: hidden;
-  width: 120px;
-  height: auto;
-  background: #26282f;
-  box-shadow: 0 8px 20px 0px #00000088;
-  color: white;
-`;
-
-const DropdownOption = styled.div`
-  width: 100%;
-  height: 37px;
-  font-size: 13px;
-  cursor: pointer;
-  padding-left: 10px;
-  padding-right: 10px;
-  white-space: nowrap;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  :hover {
-    background: #ffffff22;
-  }
-  :not(:first-child) {
-    border-top: 1px solid #00000000;
-  }
-
-  :not(:last-child) {
-    border-bottom: 1px solid #ffffff15;
-  }
-
-  > i {
-    margin-right: 5px;
-    font-size: 16px;
-  }
-`;

+ 0 - 7
dashboard/src/main/home/cluster-dashboard/preview-environments/EnvironmentsList.tsx

@@ -1,7 +0,0 @@
-import React from "react";
-
-const EnvironmentsList = () => {
-  return <div>EnvironmentsList</div>;
-};
-
-export default EnvironmentsList;

+ 17 - 17
dashboard/src/main/home/cluster-dashboard/preview-environments/PreviewEnvironmentsHome.tsx

@@ -7,7 +7,7 @@ import styled from "styled-components";
 import ButtonEnablePREnvironments from "./components/ButtonEnablePREnvironments";
 import ButtonEnablePREnvironments from "./components/ButtonEnablePREnvironments";
 import { PreviewEnvironmentsHeader } from "./components/PreviewEnvironmentsHeader";
 import { PreviewEnvironmentsHeader } from "./components/PreviewEnvironmentsHeader";
 import DeploymentList from "./deployments/DeploymentList";
 import DeploymentList from "./deployments/DeploymentList";
-import EnvironmentsList from "./EnvironmentsList";
+import EnvironmentsList from "./environments/EnvironmentsList";
 
 
 type TabEnum = "repositories" | "pull_requests";
 type TabEnum = "repositories" | "pull_requests";
 
 
@@ -72,22 +72,22 @@ const PreviewEnvironmentsHome = () => {
     return <Placeholder>Something went wrong, please try again</Placeholder>;
     return <Placeholder>Something went wrong, please try again</Placeholder>;
   }
   }
 
 
-  if (!isEnabled) {
-    return (
-      <>
-        <PreviewEnvironmentsHeader />
-        <LineBreak />
-        <Placeholder>
-          <Title>Preview environments are not enabled on this cluster</Title>
-          <Subtitle>
-            In order to use preview environments, you must enable preview
-            environments on this cluster.
-          </Subtitle>
-          <ButtonEnablePREnvironments />
-        </Placeholder>
-      </>
-    );
-  }
+  // if (!isEnabled) {
+  //   return (
+  //     <>
+  //       <PreviewEnvironmentsHeader />
+  //       <LineBreak />
+  //       <Placeholder>
+  //         <Title>Preview environments are not enabled on this cluster</Title>
+  //         <Subtitle>
+  //           In order to use preview environments, you must enable preview
+  //           environments on this cluster.
+  //         </Subtitle>
+  //         <ButtonEnablePREnvironments />
+  //       </Placeholder>
+  //     </>
+  //   );
+  // }
 
 
   return (
   return (
     <>
     <>

+ 1 - 1
dashboard/src/main/home/cluster-dashboard/preview-environments/deployments/DeploymentCard.tsx

@@ -1,6 +1,6 @@
 import React, { useState } from "react";
 import React, { useState } from "react";
 import styled, { keyframes } from "styled-components";
 import styled, { keyframes } from "styled-components";
-import { Environment, PRDeployment } from "./DeploymentList";
+import { Environment, PRDeployment } from "../types";
 import pr_icon from "assets/pull_request_icon.svg";
 import pr_icon from "assets/pull_request_icon.svg";
 import { integrationList } from "shared/common";
 import { integrationList } from "shared/common";
 import { useRouteMatch } from "react-router";
 import { useRouteMatch } from "react-router";

+ 2 - 26
dashboard/src/main/home/cluster-dashboard/preview-environments/deployments/DeploymentList.tsx

@@ -11,33 +11,9 @@ import ButtonEnablePREnvironments from "../components/ButtonEnablePREnvironments
 import ConnectNewRepo from "../components/ConnectNewRepo";
 import ConnectNewRepo from "../components/ConnectNewRepo";
 import Loading from "components/Loading";
 import Loading from "components/Loading";
 
 
-import _, { flatMapDepth } from "lodash";
+import _ from "lodash";
 import EnvironmentCard from "./DeploymentCard";
 import EnvironmentCard from "./DeploymentCard";
-
-export type PRDeployment = {
-  id: number;
-  created_at: string;
-  updated_at: string;
-  subdomain: string;
-  status: string;
-  environment_id: number;
-  pull_request_id: number;
-  namespace: string;
-  gh_pr_name: string;
-  gh_repo_owner: string;
-  gh_repo_name: string;
-  gh_commit_sha: string;
-};
-
-export type Environment = {
-  id: Number;
-  project_id: number;
-  cluster_id: number;
-  git_installation_id: number;
-  name: string;
-  git_repo_owner: string;
-  git_repo_name: string;
-};
+import { Environment, PRDeployment } from "../types";
 
 
 const DeploymentList = () => {
 const DeploymentList = () => {
   const [isLoading, setIsLoading] = useState(true);
   const [isLoading, setIsLoading] = useState(true);

+ 94 - 0
dashboard/src/main/home/cluster-dashboard/preview-environments/environments/EnvironmentCard.tsx

@@ -0,0 +1,94 @@
+import React from "react";
+import { capitalize } from "shared/string_utils";
+import styled from "styled-components";
+import { Environment } from "../types";
+import Options from "components/OptionsDropdown";
+
+type Props = {
+  environment: Environment;
+};
+
+const EnvironmentCard = ({ environment }: Props) => {
+  const {
+    name,
+    deployment_count,
+    git_repo_owner,
+    git_repo_name,
+    last_deployment_status,
+    mode,
+  } = environment;
+
+  return (
+    <EnvironmentCardWrapper>
+      <DataContainer>
+        <RepoName>{name}</RepoName>
+        <Status>
+          <StatusDot status={last_deployment_status} />
+          {capitalize(last_deployment_status || "")}
+        </Status>
+      </DataContainer>
+      <Options.Dropdown>
+        <Options.Option>Delete</Options.Option>
+      </Options.Dropdown>
+    </EnvironmentCardWrapper>
+  );
+};
+
+export default EnvironmentCard;
+
+const EnvironmentCardWrapper = styled.div`
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  border: 1px solid #ffffff44;
+  background: #ffffff08;
+  margin-bottom: 5px;
+  border-radius: 10px;
+  padding: 14px;
+  overflow: hidden;
+  height: 80px;
+  font-size: 13px;
+  animation: fadeIn 0.5s;
+  @keyframes fadeIn {
+    from {
+      opacity: 0;
+    }
+    to {
+      opacity: 1;
+    }
+  }
+`;
+
+const DataContainer = styled.div`
+  display: flex;
+  flex-direction: column;
+`;
+
+const RepoName = styled.div`
+  display: flex;
+  font-size: 20px;
+`;
+
+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: 15px;
+  background: ${(props: { status: string }) =>
+    props.status === "created"
+      ? "#4797ff"
+      : props.status === "failed"
+      ? "#ed5f85"
+      : props.status === "completed"
+      ? "#00d12a"
+      : "#f5cb42"};
+  border-radius: 20px;
+  margin-left: 3px;
+`;

+ 76 - 0
dashboard/src/main/home/cluster-dashboard/preview-environments/environments/EnvironmentsList.tsx

@@ -0,0 +1,76 @@
+import Loading from "components/Loading";
+import React, { useContext, useEffect, useState } from "react";
+import api from "shared/api";
+import { Context } from "shared/Context";
+import { deployments, environments } from "../mocks";
+import { Environment } from "../types";
+import EnvironmentCard from "./EnvironmentCard";
+
+const EnvironmentsList = () => {
+  const { currentCluster, currentProject } = useContext(Context);
+  const [isLoading, setIsLoading] = useState(true);
+  const [hasError, setHasError] = useState(false);
+  const [environments, setEnvironments] = useState<Environment[]>([]);
+
+  useEffect(() => {
+    let isSubscribed = true;
+    // api
+    //   .listEnvironments<Environment[]>(
+    //     "<token>",
+    //     {},
+    //     {
+    //       project_id: currentProject?.id,
+    //       cluster_id: currentCluster?.id,
+    //     }
+    //   )
+    mockRequest()
+      .then(({ data }) => {
+        if (!isSubscribed) {
+          return;
+        }
+
+        if (!Array.isArray(data)) {
+          throw Error("Data is not an array");
+        }
+
+        setEnvironments(data);
+      })
+
+      .catch((err) => {
+        console.error(err);
+        if (isSubscribed) {
+          setHasError(true);
+        }
+      })
+      .finally(() => {
+        if (isSubscribed) {
+          setIsLoading(false);
+        }
+      });
+
+    return () => {
+      isSubscribed = false;
+    };
+  }, [currentCluster, currentProject]);
+
+  if (isLoading) {
+    return <Loading />;
+  }
+
+  return (
+    <>
+      {environments.map((env) => (
+        <EnvironmentCard key={env.id} environment={env} />
+      ))}
+    </>
+  );
+};
+
+export default EnvironmentsList;
+
+const mockRequest = () =>
+  new Promise((res) => {
+    setTimeout(() => {
+      res({ data: environments });
+    }, 1000);
+  });

+ 152 - 0
dashboard/src/main/home/cluster-dashboard/preview-environments/mocks.ts

@@ -0,0 +1,152 @@
+export const environments = [
+  {
+    id: 29,
+    project_id: 3,
+    cluster_id: 34,
+    git_installation_id: 22158312,
+    git_repo_owner: "porter-dev",
+    git_repo_name: "porter-docs",
+    name: "Preview",
+  },
+  {
+    id: 36,
+    project_id: 3,
+    cluster_id: 34,
+    git_installation_id: 21704327,
+    git_repo_owner: "jnfrati",
+    git_repo_name: "angular-todo-app",
+    name: "Preview",
+  },
+  {
+    id: 37,
+    project_id: 3,
+    cluster_id: 34,
+    git_installation_id: 21704327,
+    git_repo_owner: "jnfrati",
+    git_repo_name: "porter-docs",
+    name: "Preview",
+  },
+  {
+    id: 38,
+    project_id: 3,
+    cluster_id: 34,
+    git_installation_id: 21704327,
+    git_repo_owner: "jnfrati",
+    git_repo_name: "porter-docs",
+    name: "Preview",
+  },
+  {
+    id: 39,
+    project_id: 3,
+    cluster_id: 34,
+    git_installation_id: 21704327,
+    git_repo_owner: "jnfrati",
+    git_repo_name: "multi-tenant-blog",
+    name: "Preview",
+  },
+  {
+    id: 40,
+    project_id: 3,
+    cluster_id: 34,
+    git_installation_id: 18424822,
+    git_repo_owner: "sunguroku",
+    git_repo_name: "node",
+    name: "Preview",
+  },
+  {
+    id: 41,
+    project_id: 3,
+    cluster_id: 34,
+    git_installation_id: 18424822,
+    git_repo_owner: "sunguroku",
+    git_repo_name: "code-server",
+    name: "Preview",
+  },
+  {
+    id: 42,
+    project_id: 3,
+    cluster_id: 34,
+    git_installation_id: 22158312,
+    git_repo_owner: "porter-dev",
+    git_repo_name: "preview-env",
+    name: "Preview",
+  },
+  {
+    id: 43,
+    project_id: 3,
+    cluster_id: 34,
+    git_installation_id: 22158312,
+    git_repo_owner: "porter-dev",
+    git_repo_name: "preview",
+    name: "Preview",
+  },
+  {
+    id: 44,
+    project_id: 3,
+    cluster_id: 34,
+    git_installation_id: 22158312,
+    git_repo_owner: "porter-dev",
+    git_repo_name: "preview-env-test",
+    name: "Preview",
+  },
+  {
+    id: 45,
+    project_id: 3,
+    cluster_id: 34,
+    git_installation_id: 22158312,
+    git_repo_owner: "porter-dev",
+    git_repo_name: "ptrtr",
+    name: "Preview",
+  },
+];
+
+export const deployments = [
+  {
+    gh_deployment_id: 534980099,
+    gh_pr_name: "Update porter.yaml",
+    gh_repo_name: "preview",
+    gh_repo_owner: "porter-dev",
+    gh_commit_sha: "74a1191",
+    id: 43,
+    created_at: "2022-03-28T19:28:11.012729Z",
+    updated_at: "2022-03-28T19:31:53.871666Z",
+    git_installation_id: 0,
+    environment_id: 43,
+    namespace: "pr-3-preview",
+    status: "failed",
+    subdomain: "",
+    pull_request_id: 3,
+  },
+  {
+    gh_deployment_id: 532608734,
+    gh_pr_name: "Testing pr preview",
+    gh_repo_name: "porter-docs",
+    gh_repo_owner: "jnfrati",
+    gh_commit_sha: "6a4b67e",
+    id: 41,
+    created_at: "2022-03-24T20:24:17.103471Z",
+    updated_at: "2022-03-24T20:45:06.684096Z",
+    git_installation_id: 0,
+    environment_id: 37,
+    namespace: "pr-1-porter-docs",
+    status: "failed",
+    subdomain: "https://docs-web-7b93751b98e68139.staging-onporter.run",
+    pull_request_id: 1,
+  },
+  {
+    gh_deployment_id: 514002155,
+    gh_pr_name: "Testing PR with job run",
+    gh_repo_name: "porter-docs",
+    gh_repo_owner: "porter-dev",
+    gh_commit_sha: "443d930",
+    id: 32,
+    created_at: "2022-01-30T11:04:14.496147Z",
+    updated_at: "2022-02-24T22:02:27.17928Z",
+    git_installation_id: 0,
+    environment_id: 29,
+    namespace: "pr-20-porter-docs",
+    status: "created",
+    subdomain: "https://docs-web-78a048205ac7869b.staging-onporter.run",
+    pull_request_id: 20,
+  },
+];

+ 27 - 0
dashboard/src/main/home/cluster-dashboard/preview-environments/types.ts

@@ -0,0 +1,27 @@
+export type PRDeployment = {
+  id: number;
+  created_at: string;
+  updated_at: string;
+  subdomain: string;
+  status: "creating" | "failed" | "created" | "inactive";
+  environment_id: number;
+  pull_request_id: number;
+  namespace: string;
+  gh_pr_name: string;
+  gh_repo_owner: string;
+  gh_repo_name: string;
+  gh_commit_sha: string;
+};
+
+export type Environment = {
+  id: number;
+  project_id: number;
+  cluster_id: number;
+  git_installation_id: number;
+  name: string;
+  git_repo_owner: string;
+  git_repo_name: string;
+  last_deployment_status: "failed" | "created" | "inactive" | "disabled";
+  deployment_count: number;
+  mode: "manual" | "auto";
+};