فهرست منبع

initial commit

Feroze Mohideen 2 سال پیش
والد
کامیت
9535acffff

+ 2 - 3
dashboard/src/main/home/app-dashboard/create-app/CreateApp.tsx

@@ -440,9 +440,8 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
                             }
                           >
                             {detectedServices.count > 0
-                              ? `Detected ${detectedServices.count} service${
-                                  detectedServices.count > 1 ? "s" : ""
-                                } from porter.yaml.`
+                              ? `Detected ${detectedServices.count} service${detectedServices.count > 1 ? "s" : ""
+                              } from porter.yaml.`
                               : `Could not detect any services from porter.yaml. Make sure it exists in the root of your repo.`}
                           </Text>
                         </AppearingDiv>

+ 5 - 0
dashboard/src/main/home/app-dashboard/expanded-app/ExpandedApp.tsx

@@ -49,6 +49,7 @@ import EventFocusView from "./activity-feed/events/focus-views/EventFocusView";
 import HelmValuesTab from "./HelmValuesTab";
 import SettingsTab from "./SettingsTab";
 import PorterAppRevisionSection from "./PorterAppRevisionSection";
+import NotificationFeed from "./notifications/NotificationFeed";
 
 type Props = RouteComponentProps & {};
 
@@ -72,6 +73,7 @@ const validTabs = [
   "settings",
   "helm-values",
   "job-history",
+  "notifications",
 ] as const;
 const DEFAULT_TAB = "activity";
 type ValidTab = typeof validTabs[number];
@@ -726,6 +728,8 @@ const ExpandedApp: React.FC<Props> = ({ ...props }) => {
           jobName={queryParamOpts.service}
           goBack={() => setExpandedJob(null)}
         />;
+      case "notifications":
+        return <NotificationFeed appName={appData.app.name} />;
       default:
         return <ActivityFeed
           chart={appData.chart}
@@ -922,6 +926,7 @@ const ExpandedApp: React.FC<Props> = ({ ...props }) => {
               <TabSelector
                 noBuffer
                 options={[
+                  { label: "Notifications", value: "notifications" },
                   { label: "Activity", value: "activity" },
                   { label: "Overview", value: "overview" },
                   hasBuiltImage && { label: "Logs", value: "logs" },

+ 36 - 0
dashboard/src/main/home/app-dashboard/expanded-app/notifications/NotificationExpandedView.tsx

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

+ 87 - 0
dashboard/src/main/home/app-dashboard/expanded-app/notifications/NotificationFeed.tsx

@@ -0,0 +1,87 @@
+import React, { useContext, useEffect, useMemo, useState } from "react";
+import styled from "styled-components";
+import NotificationList from "./NotificationList";
+import NotificationExpandedView from "./NotificationExpandedView";
+import { useQuery } from "@tanstack/react-query";
+import { PorterAppEvent, PorterAppEventType } from "../activity-feed/events/types";
+import api from "shared/api";
+import { Context } from "shared/Context";
+import Loading from "components/Loading";
+
+type Props = {
+    appName: string
+};
+
+const NotificationFeed: React.FC<Props> = ({
+    appName,
+}) => {
+    const [selectedEventId, setSelectedEventId] = useState<string>("");
+    const { currentCluster, currentProject } = useContext(Context);
+
+    const handleTileClick = (tileId: string) => {
+        setSelectedEventId(tileId);
+    };
+
+    const { data: events = [], isLoading } = useQuery<PorterAppEvent[]>(
+        [
+            "getFeedEvents",
+            currentCluster?.id,
+            currentProject?.id,
+            appName,
+        ],
+        async () => {
+            if (!currentProject?.id || !currentCluster?.id) {
+                return [];
+            }
+
+            const res = await api.getFeedEvents(
+                "<token>",
+                {},
+                {
+                    cluster_id: currentCluster.id,
+                    project_id: currentProject.id,
+                    stack_name: appName,
+                }
+            );
+            return res.data.events?.map((event: any) => PorterAppEvent.toPorterAppEvent(event)).filter((evt: PorterAppEvent) => evt.type === PorterAppEventType.APP_EVENT) ?? []
+        },
+        {
+            refetchOnWindowFocus: false,
+            enabled: !!currentCluster?.id && !!currentProject?.id
+        }
+    );
+
+    useEffect(() => {
+        if (events.length > 0) {
+            setSelectedEventId(events[0].id);
+        }
+    }, [events])
+
+    if (isLoading) {
+        return <Loading />;
+    }
+
+    return (
+        <StyledNotificationFeed>
+            <NotificationList onTileClick={handleTileClick} events={events} selectedEventId={selectedEventId} appName={appName} />
+            {selectedEventId !== "" && <NotificationExpandedView eventId={selectedEventId} />}
+        </StyledNotificationFeed>
+    );
+};
+
+export default NotificationFeed;
+
+const StyledNotificationFeed = styled.div`
+    display: flex;
+    height: 600px;
+    width: 100%;
+    animation: fadeIn 0.3s 0s;
+    @keyframes fadeIn {
+    from {
+        opacity: 0;
+    }
+    to {
+        opacity: 1;
+    }
+    }
+`;

+ 60 - 0
dashboard/src/main/home/app-dashboard/expanded-app/notifications/NotificationList.tsx

@@ -0,0 +1,60 @@
+import React, { useEffect, useState } from "react";
+import styled from "styled-components";
+import { PorterAppEvent } from "../activity-feed/events/types";
+import NotificationTile from "./NotificationTile";
+
+type Props = {
+    onTileClick: (tileId: string) => void;
+    events: PorterAppEvent[];
+    selectedEventId: string;
+    appName: string;
+};
+
+const NotificationList: React.FC<Props> = ({
+    onTileClick,
+    events,
+    selectedEventId,
+    appName,
+}) => {
+    return (
+        <StyledNotificationList>
+            {events.map((evt) => (
+                <NotificationTile key={evt.id} event={evt} selected={evt.id === selectedEventId} onClick={() => onTileClick(evt.id)} appName={appName} />
+            ))}
+        </StyledNotificationList>
+    );
+};
+
+export default NotificationList;
+
+const StyledNotificationList = styled.div`
+    width: 300px;
+    display: flex;
+    flex-direction: column;
+    height: 600px;
+    overflow: auto;
+
+    ::-webkit-scrollbar {
+        width: 3px;
+        :horizontal {
+          height: 3px;
+        }
+      }
+    
+      ::-webkit-scrollbar-corner {
+        width: 3px;
+        background: #ffffff11;
+        color: white;
+      }
+    
+      ::-webkit-scrollbar-track {
+        width: 3px;
+        -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;
+      }
+`;

+ 80 - 0
dashboard/src/main/home/app-dashboard/expanded-app/notifications/NotificationTile.tsx

@@ -0,0 +1,80 @@
+import React, { useEffect, useState } from "react";
+import styled from "styled-components";
+import { PorterAppEvent } from "../activity-feed/events/types";
+import Spacer from "components/porter/Spacer";
+import Text from "components/porter/Text";
+import { getServiceNameFromPodNameAndAppName } from "../logs/utils";
+
+type Props = {
+  event: PorterAppEvent;
+  selected: boolean;
+  onClick: () => void;
+  appName: string;
+};
+
+const NotificationTile: React.FC<Props> = ({
+  event,
+  selected,
+  onClick,
+  appName,
+}) => {
+  return (
+    <StyledNotificationTile onClick={onClick} selected={selected}>
+      <NotificationContent>
+        <NotificationSummary>{event.metadata.summary}</NotificationSummary>
+        <Spacer y={0.5} />
+        <Text color="helper">Service: <ServiceName>{getServiceNameFromPodNameAndAppName(event.metadata.pod_name, appName)}</ServiceName></Text>
+      </NotificationContent>
+    </StyledNotificationTile>
+  );
+};
+
+export default NotificationTile;
+
+const StyledNotificationTile = styled.div<{ selected?: boolean }>`
+  align-items: center;
+  user-select: none;
+  display: flex;
+  padding: 15px 10px;
+  flex-direction: column;
+  align-item: center;
+  justify-content: space-between;
+  height: 200px;
+  cursor: pointer;
+  position: relative;
+
+  border-radius: 5px;
+  background: ${props => props.selected ? props.theme.clickable.clickedBg : props.theme.clickable.bg};
+  border: ${props => props.selected ? "1px solid #fff" : "1px solid #494b4f"};
+  :hover {
+    border: ${({ selected }) => (!selected && "1px solid #7a7b80")};
+  }
+
+  animation: fadeIn 0.3s 0s;
+  @keyframes fadeIn {
+    from {
+      opacity: 0;
+    }
+    to {
+      opacity: 1;
+    }
+  }
+`;
+
+const NotificationContent = styled.div`
+  display: flex;
+  flex-direction: column;
+  align-items: flex-start;
+  width: 100%;
+`;
+
+const NotificationSummary = styled.div`
+  color: #ffffff;
+  font-size: 13px;
+  font-weight: 500;
+`;
+
+const ServiceName = styled.span`
+  color: #ffffff;
+`;
+

+ 1 - 0
dashboard/src/shared/themes/midnight.ts

@@ -6,6 +6,7 @@ const theme = {
   button: "#3A48CA",
   clickable: {
     bg: "linear-gradient(180deg, #171B21, #121212)",
+    clickedBg: "linear-gradient(180deg, #2C2E32, #24262A)",
   },
   modalBg: "#171B2111",
   text: {