Browse Source

Activity feed (#3057)

* activity feed boilerplate fe

* hide activity feed

---------

Co-authored-by: Justin Rhee <jusrhee@Justins-MacBook-Air.local>
jusrhee 3 years ago
parent
commit
656c642b74
27 changed files with 319 additions and 48 deletions
  1. BIN
      dashboard/src/assets/app_event.png
  2. BIN
      dashboard/src/assets/build.png
  3. BIN
      dashboard/src/assets/deploy.png
  4. BIN
      dashboard/src/assets/failure.png
  5. BIN
      dashboard/src/assets/pre_deploy.png
  6. BIN
      dashboard/src/assets/run_for.png
  7. 0 1
      dashboard/src/components/Button.tsx
  8. 0 1
      dashboard/src/components/ConfirmOverlay.tsx
  9. 0 1
      dashboard/src/components/DocsHelper.tsx
  10. 0 2
      dashboard/src/components/MultiSaveButton.tsx
  11. 0 1
      dashboard/src/components/PageNotFound.tsx
  12. 0 2
      dashboard/src/components/ProvisionerStatus.tsx
  13. 0 2
      dashboard/src/components/ResourceTab.tsx
  14. 22 0
      dashboard/src/components/porter/Icon.tsx
  15. 5 2
      dashboard/src/components/porter/Link.tsx
  16. 1 0
      dashboard/src/hosted.index.html
  17. 1 0
      dashboard/src/index.html
  18. 7 4
      dashboard/src/main/home/app-dashboard/expanded-app/ExpandedApp.tsx
  19. 59 22
      dashboard/src/main/home/app-dashboard/expanded-app/activity-feed/ActivityFeed.tsx
  20. 224 0
      dashboard/src/main/home/app-dashboard/expanded-app/activity-feed/EventCard.tsx
  21. 0 1
      dashboard/src/main/home/project-settings/ProjectSettings.tsx
  22. 0 1
      dashboard/src/main/home/project-settings/api-tokens/CreateAPITokenForm.tsx
  23. 0 1
      dashboard/src/main/home/project-settings/api-tokens/TokenList.tsx
  24. 0 1
      dashboard/src/main/home/sidebar/ClusterSection.tsx
  25. 0 1
      dashboard/src/main/home/sidebar/Clusters.tsx
  26. 0 1
      dashboard/src/main/home/sidebar/ProjectSection.tsx
  27. 0 4
      dashboard/src/main/home/sidebar/Sidebar.tsx

BIN
dashboard/src/assets/app_event.png


BIN
dashboard/src/assets/build.png


BIN
dashboard/src/assets/deploy.png


BIN
dashboard/src/assets/failure.png


BIN
dashboard/src/assets/pre_deploy.png


BIN
dashboard/src/assets/run_for.png


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

@@ -30,7 +30,6 @@ const ButtonWrapper = styled.div`
   justify-content: space-between;
   font-size: 13px;
   cursor: pointer;
-  font-family: "Work Sans", sans-serif;
   color: white;
   font-weight: 500;
   padding: 10px;

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

@@ -77,7 +77,6 @@ const ConfirmButton = styled.div`
   width: 80px;
   cursor: pointer;
   opacity: 0;
-  font-family: "Work Sans", sans-serif;
   font-size: 18px;
   font-weight: 500;
   animation: linEnter 0.3s 0.1s;

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

@@ -115,7 +115,6 @@ const Tooltip = styled.div<{ placement: TooltipPlacement }>`
 `;
 
 const StyledContent = styled.div`
-  font-family: "Work Sans", sans-serif;
   font-size: 12px;
   font-weight: normal;
   padding: 12px 14px;

+ 0 - 2
dashboard/src/components/MultiSaveButton.tsx

@@ -173,7 +173,6 @@ type StatusWrapperProps = {
 const StatusWrapper = styled.div<StatusWrapperProps>`
   display: flex;
   align-items: center;
-  font-family: "Work Sans", sans-serif;
   font-size: 13px;
   color: #ffffff55;
   ${(props) => {
@@ -249,7 +248,6 @@ const Button = styled.button<ButtonProps>`
   height: 35px;
   font-size: 13px;
   font-weight: 500;
-  font-family: "Work Sans", sans-serif;
   color: white;
   display: flex;
   align-items: center;

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

@@ -143,7 +143,6 @@ const BackButton = styled.div`
 `;
 
 const StyledPageNotFound = styled.div`
-  font-family: "Work Sans", sans-serif;
   color: #6f6f6f;
   font-size: 16px;
   user-select: none;

+ 0 - 2
dashboard/src/components/ProvisionerStatus.tsx

@@ -924,7 +924,6 @@ const DeleteAction = styled.span`
   height: 35px;
   font-size: 13px;
   font-weight: 500;
-  font-family: "Work Sans", sans-serif;
   display: flex;
   align-items: center;
   justify-content: space-between;
@@ -944,7 +943,6 @@ const Button = styled.button`
   font-size: 13px;
   margin: 10px 0;
   font-weight: 500;
-  font-family: "Work Sans", sans-serif;
   color: white;
   padding: 6px 20px 7px 20px;
   text-align: left;

+ 0 - 2
dashboard/src/components/ResourceTab.tsx

@@ -165,7 +165,6 @@ const Tooltip = styled.div`
   color: white;
   text-transform: none;
   font-size: 12px;
-  font-family: "Work Sans", sans-serif;
   outline: 1px solid #ffffff55;
   opacity: 0;
   animation: faded-in 0.2s 0.15s;
@@ -229,7 +228,6 @@ const Status = styled.div`
   text-transform: capitalize;
   justify-content: flex-end;
   align-items: center;
-  font-family: 'Work Sans', sans-serif;
   color: #aaaabb;
   animation: fadeIn 0.5s;
   @keyframes fadeIn {

+ 22 - 0
dashboard/src/components/porter/Icon.tsx

@@ -0,0 +1,22 @@
+import React from "react";
+import styled from "styled-components";
+
+type Props = {
+  src: any;
+  height?: string;
+};
+
+const Icon: React.FC<Props> = ({
+  src,
+  height,
+}) => {
+  return (
+    <StyledIcon src={src} height={height} />
+  );
+};
+
+export default Icon;
+
+const StyledIcon = styled.img<{ height?: string}>`
+  height: ${props => props.height || "20px"};
+`;

+ 5 - 2
dashboard/src/components/porter/Link.tsx

@@ -44,13 +44,16 @@ export default Link;
 const Div = styled.span<{ hasunderline?: boolean }>`
   color: #ffffff;
   cursor: pointer;
+  font-size: 13px;
   display: inline;
-  text-decoration: ${props => props.hasunderline ? "underline" : ""};
+  border-bottom: ${props => props.hasunderline ? "1px solid #fff" : ""};
 `;
 
 const StyledLink = styled(DynamicLink)<{ hasunderline?: boolean }>`
   color: #ffffff;
   display: inline;
+  font-size: 13px;
   cursor: pointer;
-  text-decoration: ${props => props.hasunderline ? "underline" : ""};
+  text-decoration: ;
+  border-bottom: ${props => props.hasunderline ? "1px solid #fff" : ""};
 `;

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

@@ -141,6 +141,7 @@
       href="https://fonts.googleapis.com/css?family=Work+Sans:400,500,600"
       rel="stylesheet"
     />
+    <link href="https://fonts.cdnfonts.com/css/general-sans" rel="stylesheet">
     <link
       href="//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0/katex.min.css"
       rel="stylesheet"

+ 1 - 0
dashboard/src/index.html

@@ -21,6 +21,7 @@
       href="https://fonts.googleapis.com/css?family=Work+Sans:400,500,600"
       rel="stylesheet"
     />
+    <link href="https://fonts.cdnfonts.com/css/general-sans" rel="stylesheet">
     <link
       href="//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0/katex.min.css"
       rel="stylesheet"

+ 7 - 4
dashboard/src/main/home/app-dashboard/expanded-app/ExpandedApp.tsx

@@ -42,7 +42,7 @@ import { EnvVariablesTab } from "./EnvVariablesTab";
 import GHABanner from "./GHABanner";
 import LogSection from "./LogSection";
 import EventsTab from "./EventsTab";
-import ActivityFeed from "./ActivityFeed";
+import ActivityFeed from "./activity-feed/ActivityFeed";
 import JobRuns from "./JobRuns";
 import MetricsSection from "./MetricsSection";
 import StatusSectionFC from "./status/StatusSection";
@@ -838,13 +838,14 @@ const ExpandedApp: React.FC<Props> = ({ ...props }) => {
                     ? hasBuiltImage
                       ? [
                         { label: "Overview", value: "overview" },
+                        // { label: "Activity", value: "activity" },
                         { label: "Events", value: "events" },
                         { label: "Logs", value: "logs" },
                         { label: "Metrics", value: "metrics" },
                         { label: "Debug", value: "status" },
                         { label: "Pre-deploy", value: "pre-deploy" },
                         {
-                          label: "Environment variables",
+                          label: "Environment",
                           value: "environment-variables",
                         },
                         { label: "Build settings", value: "build-settings" },
@@ -852,9 +853,10 @@ const ExpandedApp: React.FC<Props> = ({ ...props }) => {
                       ]
                       : [
                         { label: "Overview", value: "overview" },
+                        // { label: "Activity", value: "activity" },
                         { label: "Pre-deploy", value: "pre-deploy" },
                         {
-                          label: "Environment variables",
+                          label: "Environment",
                           value: "environment-variables",
                         },
                         { label: "Build settings", value: "build-settings" },
@@ -862,13 +864,14 @@ const ExpandedApp: React.FC<Props> = ({ ...props }) => {
                       ]
                     : [
                       { label: "Overview", value: "overview" },
+                      // { label: "Activity", value: "activity" },
                       { label: "Events", value: "events" },
                       { label: "Logs", value: "logs" },
                       { label: "Metrics", value: "metrics" },
                       { label: "Debug", value: "status" },
                       { label: "Pre-deploy", value: "pre-deploy" },
                       {
-                        label: "Environment variables",
+                        label: "Environment",
                         value: "environment-variables",
                       },
                       { label: "Settings", value: "settings" },

+ 59 - 22
dashboard/src/main/home/app-dashboard/expanded-app/ActivityFeed.tsx → dashboard/src/main/home/app-dashboard/expanded-app/activity-feed/ActivityFeed.tsx

@@ -6,7 +6,8 @@ import { Context } from "shared/Context";
 
 import Text from "components/porter/Text";
 import Container from "components/porter/Container";
-import VerticalSteps from "components/porter/VerticalSteps";
+
+import EventCard from "./EventCard";
 
 type Props = {
   chart: any;
@@ -14,11 +15,53 @@ type Props = {
 
 const dummyEvents = [
   {
-    type: "build",
+    "id": 0,
+    "status": "SUCCESS",
+    "type": "BUILD",
+    "type_external_source": "GITHUB",
+    "created_at": "",
+    "updated_at": "",
+    "porter_app_id": 0,
+    "metadata": {
+      // keys depend on "type". See below
+    }
+  },
+  {
+    "id": 0,
+    "status": "FAILED",
+    "type": "DEPLOY",
+    "type_external_source": "KUBERNETES",
+    "created_at": "",
+    "updated_at": "",
+    "porter_app_id": 0,
+    "metadata": {
+      // keys depend on "type". See below
+    }
+  },
+  {
+    "id": 0,
+    "status": "PROGRESSING",
+    "type": "PRE_DEPLOY",
+    "type_external_source": "KUBERNETES",
+    "created_at": "",
+    "updated_at": "",
+    "porter_app_id": 0,
+    "metadata": {
+      // keys depend on "type". See below
+    }
   },
   {
-    type: "deploy",
-  }
+    "id": 0,
+    "status": "FAILED",
+    "type": "APP_EVENT",
+    "type_external_source": "KUBERNETES",
+    "created_at": "",
+    "updated_at": "",
+    "porter_app_id": 0,
+    "metadata": {
+      // keys depend on "type". See below
+    }
+  },
 ]
 
 const ActivityFeed: React.FC<Props> = ({
@@ -30,14 +73,6 @@ const ActivityFeed: React.FC<Props> = ({
     // Do something
   }, []);
 
-  const renderEvent = (event: any) => {
-    return (
-      <EventCard>
-        some event
-      </EventCard>
-    )
-  };
-
   return (
     <StyledActivityFeed>
       {dummyEvents.map((event, i) => {
@@ -48,7 +83,11 @@ const ActivityFeed: React.FC<Props> = ({
           >
             {(i !== dummyEvents.length - 1) && <Line />}
             <Dot />
-            {renderEvent(event)}
+            <Time>
+              <Text>Jun 16</Text>
+              <Text>12:00 PM</Text>
+            </Time>
+            <EventCard event={event} />
           </EventWrapper>
         );
       })}
@@ -58,21 +97,17 @@ const ActivityFeed: React.FC<Props> = ({
 
 export default ActivityFeed;
 
-const EventCard = styled.div`
-  width: 100%;
-  padding: 20px;
-  border-radius: 5px;
-  background: ${({ theme }) => theme.fg};
-  border: 1px solid ${({ theme }) => theme.border};
+const Time = styled.div`
+  margin-right: -5px;
 `;
 
 const Line = styled.div`
   width: 1px;
-  height: calc(100% + 35px);
+  height: calc(100% + 30px);
   background: #414141;
   position: absolute;
   left: 3px;
-  top: 8px;
+  top: 36px;
   opacity: 1;
 `;
 
@@ -83,7 +118,7 @@ const Dot = styled.div`
   border-radius: 50%;
   position: absolute;
   left: 0;
-  top: 7px;
+  top: 36px;
   opacity: 1;
 `;
 
@@ -91,6 +126,8 @@ const EventWrapper = styled.div<{
   isLast: boolean;
 }>`
   padding-left: 30px;
+  display: flex;
+  align-items: center;
   position: relative;
   margin-bottom: ${props => props.isLast ? "" : "25px"};
 `;

+ 224 - 0
dashboard/src/main/home/app-dashboard/expanded-app/activity-feed/EventCard.tsx

@@ -0,0 +1,224 @@
+import React from "react";
+import styled from "styled-components";
+
+import app_event from "assets/app_event.png";
+import build from "assets/build.png";
+import deploy from "assets/deploy.png";
+import pre_deploy from "assets/pre_deploy.png";
+import loading from "assets/loading.gif";
+import healthy from "assets/status-healthy.png";
+import failure from "assets/failure.png";
+import run_for from "assets/run_for.png";
+import refresh from "assets/refresh.png";
+
+import Text from "components/porter/Text";
+import Container from "components/porter/Container";
+import Spacer from "components/porter/Spacer";
+import Link from "components/porter/Link";
+import Icon from "components/porter/Icon";
+
+type Props = {
+  event: any;
+};
+
+const EventCard: React.FC<Props> = ({
+  event,
+}) => {
+  const getIcon = (eventType: string) => {
+    switch (eventType) {
+      case "APP_EVENT":
+        return app_event;
+      case "BUILD":
+        return build;
+      case "DEPLOY":
+        return deploy;
+      case "PRE_DEPLOY":
+        return pre_deploy;
+      default:
+        return app_event;
+    };
+  };
+
+  const getTitle = (eventType: string) => {
+    switch (eventType) {
+      case "APP_EVENT":
+        return "Some application event";
+      case "BUILD":
+        return "Application build";
+      case "DEPLOY":
+        return "Application deploy";
+      case "PRE_DEPLOY":
+        return "Application pre-deploy";
+      default:
+        return "";
+    };
+  };
+
+  const getStatusIcon = (status: string) => {
+    switch (status) {
+      case "SUCCESS":
+        return healthy;
+      case "FAILED":
+        return failure;
+      case "PROGRESSING":
+        return loading;
+      default:
+        return loading;
+    };
+  };
+
+  const renderStatusText = (event: any) => {
+    if (event.type === "BUILD") {
+      switch (event.status) {
+        case "SUCCESS":
+          return <Text color="#68BF8B">Build succeeded</Text>
+        case "FAILED":
+          return <Text color="#FF6060">Build failed</Text>
+        default:
+          return <Text color="#aaaabb66">Build in progress . . </Text>
+      };
+    };
+    
+    if (event.type === "DEPLOY") {
+      switch (event.status) {
+        case "SUCCESS":
+          return <Text color="#68BF8B">Deployed v100</Text>
+        case "FAILED":
+          return <Text color="#FF6060">Deploying v100 failed</Text>
+        default:
+          return <Text color="#aaaabb66">Deploying v100 . . .</Text>
+      };
+    };
+    
+    if (event.type === "PRE_DEPLOY") {
+      switch (event.status) {
+        case "SUCCESS":
+          return <Text color="#68BF8B">Pre-deploy succeeded . . </Text>
+        case "FAILED":
+          return <Text color="#FF6060">Pre-deploy failed . . </Text>
+        default:
+          return <Text color="#aaaabb66">Pre-deploy in progress . . </Text>
+      };
+    };
+  };
+
+  const renderInfoCta = (event: any) => {
+    if (event.type === "APP_EVENT") {
+      return (
+        <>
+          <Link hasunderline onClick={() => alert("TODO: open details modal")}>View details</Link>
+          <Spacer inline x={1} />
+        </>
+      );
+    };
+
+    if (event.type === "BUILD") {
+      switch (event.status) {
+        case "SUCCESS":
+          return (
+            <>
+              <Link hasunderline onClick={() => alert("TODO: open GHA logs modal")}>View logs</Link>
+              <Spacer inline x={1} />
+            </>
+          );
+        case "FAILED":
+          return (
+            <>
+              <Link hasunderline onClick={() => alert("TODO: open GHA logs modal")}>View logs</Link>
+              <Spacer inline x={1} />
+            </>
+          );
+        default:
+          return (
+            <>
+              <Link hasunderline onClick={() => alert("TODO: link to GHA")}>View live logs</Link>
+              <Spacer inline x={1} />
+            </>
+          );
+      };
+    };
+    
+    if (event.type === "DEPLOY") {
+      if (event.type === "FAILED") {
+        return (
+          <>
+            <Link hasunderline onClick={() => alert("TODO: open deploy logs modal")}>View logs</Link>
+            <Spacer inline x={1} />
+          </>
+        );
+      } else {
+        return;
+      };
+    };
+    
+    if (event.type === "PRE_DEPLOY") {
+      return (
+        <>
+          <Link hasunderline onClick={() => alert("TODO: open logs modal")}>View logs</Link>
+          <Spacer inline x={1} />
+        </>
+      );
+    };
+  };
+
+  return (
+    <StyledEventCard>
+      <Container row spaced>
+        <Container row>
+          <Icon height="18px" src={getIcon(event.type)} />
+          <Spacer inline width="10px" />
+          <Text size={14}>{getTitle(event.type)}</Text>
+        </Container>
+        <Container row>
+          <Icon height="14px" src={run_for} />
+          <Spacer inline width="6px" />
+          <Text color="helper">1h 2m</Text>
+        </Container>
+      </Container>
+      <Spacer y={1} />
+      <Container row spaced>
+        <Container row>
+          {event.type !== "APP_EVENT" && (
+            <>
+              <Icon height="18px" src={getStatusIcon(event.status)} />
+              <Spacer inline width="10px" />
+            </>
+          )}
+          {renderStatusText(event)}
+          {event.type !== "APP_EVENT" && (
+            <Spacer inline x={1} />
+          )}
+          {renderInfoCta(event)}
+          {event.status === "FAILED" && event.type !== "APP_EVENT" && (
+            <>
+              <Link hasunderline>
+                <Container row>
+                  <Icon height="10px" src={refresh} />
+                  <Spacer inline width="5px" />
+                  Retry
+                </Container>
+              </Link>
+            </>
+          )}
+        </Container>
+        {false && (
+          <Text color="helper">user@email.com</Text>
+        )}
+      </Container>
+    </StyledEventCard>
+  );
+};
+
+export default EventCard;
+
+const StyledEventCard = styled.div`
+  width: 100%;
+  padding: 15px;
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+  height: 85px;
+  border-radius: 5px;
+  background: ${({ theme }) => theme.fg};
+  border: 1px solid ${({ theme }) => theme.border};
+`;

+ 0 - 1
dashboard/src/main/home/project-settings/ProjectSettings.tsx

@@ -236,7 +236,6 @@ const DeleteButton = styled.div`
   height: 35px;
   font-size: 13px;
   font-weight: 500;
-  font-family: "Work Sans", sans-serif;
   color: white;
   display: flex;
   align-items: center;

+ 0 - 1
dashboard/src/main/home/project-settings/api-tokens/CreateAPITokenForm.tsx

@@ -503,7 +503,6 @@ const Invalid = styled.div`
   color: #f5cb42;
   margin-left: 15px;
   font-size: 13px;
-  font-family: "Work Sans", sans-serif;
 `;
 
 const Status = styled.div<{ status: "accepted" | "expired" | "pending" }>`

+ 0 - 1
dashboard/src/main/home/project-settings/api-tokens/TokenList.tsx

@@ -208,7 +208,6 @@ const RevokeAccessButton = styled.div`
   display: inline-block;
   font-size: 13px;
   font-weight: 500;
-  font-family: "Work Sans", sans-serif;
   padding: 6px 10px;
   text-align: center;
   border: 1px solid #ffffff55;

+ 0 - 1
dashboard/src/main/home/sidebar/ClusterSection.tsx

@@ -321,7 +321,6 @@ const NavButton = styled(SidebarLink)`
   margin-left: 39px;
   padding: 0 30px 2px 8px;
   font-size: 13px;
-  font-family: "Work Sans", sans-serif;
   color: ${props => props.theme.text.primary};
   cursor: ${(props: { disabled?: boolean }) =>
     props.disabled ? "not-allowed" : "pointer"};

+ 0 - 1
dashboard/src/main/home/sidebar/Clusters.tsx

@@ -324,7 +324,6 @@ const NavButton = styled(SidebarLink)`
   margin: 5px 15px;
   padding: 0 30px 2px 8px;
   font-size: 13px;
-  font-family: "Work Sans", sans-serif;
   color: ${props => props.theme.text.primary};
   cursor: ${(props: { disabled?: boolean }) =>
     props.disabled ? "not-allowed" : "pointer"};

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

@@ -265,7 +265,6 @@ const MainSelector = styled.div`
   align-items: center;
   margin: 10px 0 0;
   font-size: 14px;
-  font-family: "Work Sans", sans-serif;
   cursor: pointer;
   padding: 10px 0;
   padding-left: 20px;

+ 0 - 4
dashboard/src/main/home/sidebar/Sidebar.tsx

@@ -290,7 +290,6 @@ const ProjectPlaceholder = styled.div`
   justify-content: center;
   height: calc(100% - 100px);
   font-size: 13px;
-  font-family: "Work Sans", sans-serif;
   color: #aaaabb;
   padding-bottom: 80px;
 
@@ -310,7 +309,6 @@ const NavButton = styled(SidebarLink)`
   margin: 5px 15px;
   padding: 0 30px 2px 6px;
   font-size: 13px;
-  font-family: "Work Sans", sans-serif;
   color: ${props => props.theme.text.primary};
   cursor: ${(props: { disabled?: boolean }) =>
     props.disabled ? "not-allowed" : "pointer"};
@@ -408,7 +406,6 @@ const Tooltip = styled.div`
   flex: 1;
   color: white;
   font-size: 12px;
-  font-family: Work Sans, sans-serif;
   outline: 1px solid #ffffff55;
   opacity: 0;
   animation: faded-in 0.2s 0.15s;
@@ -449,7 +446,6 @@ const CollapseButton = styled.div`
 `;
 
 const StyledSidebar = styled.section`
-  font-family: "Work Sans", sans-serif;
   width: 240px;
   position: relative;
   padding-top: 20px;