Просмотр исходного кода

Implemented pull request card and listing

jnfrati 4 лет назад
Родитель
Сommit
bd05e0fb44

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

@@ -12,9 +12,8 @@ import { Context } from "shared/Context";
 
 const DeploymentCard: React.FC<{
   deployment: PRDeployment;
-  environment?: Environment;
   onDelete?: () => void;
-}> = ({ deployment, environment, onDelete }) => {
+}> = ({ deployment, onDelete }) => {
   const { setCurrentOverlay, currentProject, currentCluster } = useContext(
     Context
   );
@@ -45,7 +44,7 @@ const DeploymentCard: React.FC<{
   };
 
   return (
-    <DeploymentCardWrapper key={deployment.id}>
+    <DeploymentCardWrapper>
       <DataContainer>
         <PRName>
           <PRIcon src={pr_icon} alt="pull request icon" />

+ 89 - 20
dashboard/src/main/home/cluster-dashboard/preview-environments/deployments/DeploymentList.tsx

@@ -8,9 +8,11 @@ import Loading from "components/Loading";
 
 import _ from "lodash";
 import DeploymentCard from "./DeploymentCard";
-import { Environment, PRDeployment } from "../types";
+import { Environment, PRDeployment, PullRequest } from "../types";
 import { useRouting } from "shared/routing";
 import { useHistory, useLocation } from "react-router";
+import { deployments, pull_requests } from "../mocks";
+import PullRequestCard from "./PullRequestCard";
 
 const AvailableStatusFilters = [
   "all",
@@ -18,6 +20,7 @@ const AvailableStatusFilters = [
   "failed",
   "active",
   "inactive",
+  "not_deployed",
 ];
 
 type AvailableStatusFiltersType = typeof AvailableStatusFilters[number];
@@ -26,6 +29,8 @@ const DeploymentList = ({ environments }: { environments: Environment[] }) => {
   const [isLoading, setIsLoading] = useState(true);
   const [hasError, setHasError] = useState(false);
   const [deploymentList, setDeploymentList] = useState<PRDeployment[]>([]);
+  const [pullRequests, setPullRequests] = useState<PullRequest[]>([]);
+
   const [
     statusSelectorVal,
     setStatusSelectorVal,
@@ -38,14 +43,15 @@ const DeploymentList = ({ environments }: { environments: Environment[] }) => {
   const history = useHistory();
 
   const getPRDeploymentList = () => {
-    return api.getPRDeploymentList(
-      "<token>",
-      {},
-      {
-        project_id: currentProject.id,
-        cluster_id: currentCluster.id,
-      }
-    );
+    // return api.getPRDeploymentList(
+    //   "<token>",
+    //   {},
+    //   {
+    //     project_id: currentProject.id,
+    //     cluster_id: currentCluster.id,
+    //   }
+    // );
+    return mockRequest();
   };
 
   useEffect(() => {
@@ -91,6 +97,7 @@ const DeploymentList = ({ environments }: { environments: Environment[] }) => {
         }
 
         setDeploymentList(data.deployments || []);
+        setPullRequests(data.pull_requests || []);
         setIsLoading(false);
       })
       .catch((err) => {
@@ -110,6 +117,7 @@ const DeploymentList = ({ environments }: { environments: Environment[] }) => {
     getPRDeploymentList()
       .then(({ data }) => {
         setDeploymentList(data.deployments || []);
+        setPullRequests(data.pull_requests || []);
       })
       .catch((err) => {
         setHasError(true);
@@ -118,16 +126,49 @@ const DeploymentList = ({ environments }: { environments: Environment[] }) => {
       .finally(() => setIsLoading(false));
   };
 
+  const filteredDeployments = useMemo(() => {
+    if (statusSelectorVal === "not_deployed") {
+      return [];
+    }
+
+    if (statusSelectorVal === "all" && selectedRepo === "all") {
+      return deploymentList;
+    }
+
+    let tmpDeploymentList = [...deploymentList];
+
+    if (selectedRepo !== "all") {
+      tmpDeploymentList = tmpDeploymentList.filter((deployment) => {
+        return (
+          `${deployment.gh_repo_owner}/${deployment.gh_repo_name}` ===
+          selectedRepo
+        );
+      });
+    }
+
+    if (statusSelectorVal !== "all") {
+      tmpDeploymentList = tmpDeploymentList.filter((d) => {
+        return d.status === statusSelectorVal;
+      });
+    }
+
+    return tmpDeploymentList;
+  }, [selectedRepo, statusSelectorVal, deploymentList]);
+
+  const filteredPullRequests = useMemo(() => {
+    if (selectedRepo === "all") {
+      return pullRequests;
+    }
+
+    return pullRequests.filter((pr) => {
+      return `${pr.repo_owner}/${pr.repo_name}` === selectedRepo;
+    });
+  }, [selectedRepo, pullRequests]);
+
   if (hasError) {
     return <Placeholder>Error</Placeholder>;
   }
 
-  const filteredDeployments = useMemo(() => {
-    return deploymentList.filter((d) => {
-      return d.status === statusSelectorVal;
-    });
-  }, [statusSelectorVal]);
-
   const renderDeploymentList = () => {
     if (isLoading) {
       return (
@@ -137,7 +178,7 @@ const DeploymentList = ({ environments }: { environments: Environment[] }) => {
       );
     }
 
-    if (!deploymentList.length) {
+    if (!deploymentList.length && !pullRequests.length) {
       return (
         <Placeholder>
           No preview apps have been found. Open a PR to create a new preview
@@ -146,7 +187,7 @@ const DeploymentList = ({ environments }: { environments: Environment[] }) => {
       );
     }
 
-    if (!filteredDeployments.length) {
+    if (!filteredDeployments.length && !filteredPullRequests.length) {
       return (
         <Placeholder>
           No preview apps have been found with the given filter.
@@ -154,9 +195,22 @@ const DeploymentList = ({ environments }: { environments: Environment[] }) => {
       );
     }
 
-    return filteredDeployments.map((d) => {
-      return <DeploymentCard deployment={d} onDelete={handleRefresh} />;
-    });
+    return (
+      <>
+        {filteredPullRequests.map((pr) => {
+          return <PullRequestCard key={pr.pr_title} pullRequest={pr} />;
+        })}
+        {filteredDeployments.map((d) => {
+          return (
+            <DeploymentCard
+              key={d.id}
+              deployment={d}
+              onDelete={handleRefresh}
+            />
+          );
+        })}
+      </>
+    );
   };
 
   const repoOptions = environments
@@ -212,6 +266,10 @@ const DeploymentList = ({ environments }: { environments: Environment[] }) => {
                   value: "inactive",
                   label: "Inactive",
                 },
+                {
+                  value: "not_deployed",
+                  label: "Not deployed",
+                },
               ]}
               dropdownLabel="Status"
               width="150px"
@@ -247,6 +305,17 @@ const DeploymentList = ({ environments }: { environments: Environment[] }) => {
 
 export default DeploymentList;
 
+const mockRequest = () =>
+  new Promise((res) => {
+    setTimeout(
+      () =>
+        res({
+          data: { deployments: deployments, pull_requests: pull_requests },
+        }),
+      1000
+    );
+  });
+
 const ActionsWrapper = styled.div`
   display: flex;
 `;

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

@@ -0,0 +1,238 @@
+import React, { useState } from "react";
+import styled from "styled-components";
+import pr_icon from "assets/pull_request_icon.svg";
+import { PullRequest } from "../types";
+import { integrationList } from "shared/common";
+
+const PullRequestCard = ({ pullRequest }: { pullRequest: PullRequest }) => {
+  const [showRepoTooltip, setShowRepoTooltip] = useState(false);
+
+  const repository = `${pullRequest.repo_owner}/${pullRequest.repo_name}`;
+
+  const createPreviewEnvironment = () => {};
+
+  return (
+    <DeploymentCardWrapper>
+      <DataContainer>
+        <PRName>
+          <PRIcon src={pr_icon} alt="pull request icon" />
+          {pullRequest.pr_title}
+        </PRName>
+
+        <Flex>
+          <StatusContainer>
+            <Status>
+              <StatusDot status="" />
+              Not deployed
+            </Status>
+          </StatusContainer>
+          <DeploymentImageContainer>
+            <DeploymentTypeIcon src={integrationList.repo.icon} />
+            <RepositoryName
+              onMouseOver={() => {
+                setShowRepoTooltip(true);
+              }}
+              onMouseOut={() => {
+                setShowRepoTooltip(false);
+              }}
+            >
+              {repository}
+            </RepositoryName>
+            {showRepoTooltip && <Tooltip>{repository}</Tooltip>}
+            <InfoWrapper>
+              <LastDeployed>
+                From: {pullRequest.branch_from} Into: {pullRequest.branch_into}
+              </LastDeployed>
+            </InfoWrapper>
+          </DeploymentImageContainer>
+        </Flex>
+      </DataContainer>
+      <Flex>
+        <CreatePreviewEnvironmentButton>
+          <i className="material-icons">add</i>
+          Create Preview environment
+        </CreatePreviewEnvironmentButton>
+      </Flex>
+    </DeploymentCardWrapper>
+  );
+};
+
+export default PullRequestCard;
+
+const CreatePreviewEnvironmentButton = styled.button`
+  font-size: 12px;
+  padding: 8px 10px;
+  margin-left: 10px;
+  border-radius: 5px;
+  color: #ffffff;
+  border: 1px solid #aaaabb;
+  display: flex;
+  align-items: center;
+  background: #ffffff08;
+  cursor: pointer;
+  :hover {
+    background: #ffffff22;
+  }
+
+  > i {
+    font-size: 14px;
+    margin-right: 8px;
+  }
+`;
+
+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;
+  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;
+  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: 15px;
+  background: ${(props: { status: string }) =>
+    props.status === "created"
+      ? "#4797ff"
+      : props.status === "failed"
+      ? "#ed5f85"
+      : props.status === "completed"
+      ? "#00d12a"
+      : "#f5cb42"};
+  border-radius: 20px;
+  margin-left: 3px;
+`;
+
+const DeploymentImageContainer = styled.div`
+  height: 20px;
+  font-size: 13px;
+  position: relative;
+  display: flex;
+  margin-left: 15px;
+  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: -20px;
+  top: 10px;
+  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;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  margin-right: 8px;
+`;
+
+const LastDeployed = styled.div`
+  font-size: 13px;
+  margin-left: 14px;
+  margin-top: -1px;
+  display: flex;
+  align-items: center;
+  color: #aaaabb66;
+`;

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

@@ -150,3 +150,14 @@ export const deployments = [
     pull_request_id: 20,
   },
 ];
+
+export const pull_requests = [
+  {
+    pr_title: "Testing PR with job run",
+    pr_number: 1,
+    repo_owner: "porter-docs",
+    repo_name: "porter-dev",
+    branch_from: "some_branch",
+    branch_into: "main",
+  },
+];

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

@@ -25,3 +25,12 @@ export type Environment = {
   deployment_count: number;
   mode: "manual" | "auto";
 };
+
+export type PullRequest = {
+  pr_title: string;
+  pr_number: number;
+  repo_owner: string;
+  repo_name: string;
+  branch_from: string;
+  branch_into: string;
+};